restructured folders as this project has grown
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 5 Sep 2022 00:28:09 +0000 (19:28 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 5 Sep 2022 00:28:09 +0000 (19:28 -0500)
70 files changed:
build.sh
compiler/build.sh [new file with mode: 0755]
compiler/include/astnodes.h [new file with mode: 0644]
compiler/include/doc.h [new file with mode: 0644]
compiler/include/errors.h [new file with mode: 0644]
compiler/include/lex.h [new file with mode: 0644]
compiler/include/parser.h [new file with mode: 0644]
compiler/include/types.h [new file with mode: 0644]
compiler/include/utils.h [new file with mode: 0644]
compiler/include/wasm_emit.h [new file with mode: 0644]
compiler/src/astnodes.c [new file with mode: 0644]
compiler/src/builtins.c [new file with mode: 0644]
compiler/src/checker.c [new file with mode: 0644]
compiler/src/clone.c [new file with mode: 0644]
compiler/src/doc.c [new file with mode: 0644]
compiler/src/entities.c [new file with mode: 0644]
compiler/src/errors.c [new file with mode: 0644]
compiler/src/lex.c [new file with mode: 0644]
compiler/src/onyx.c [new file with mode: 0644]
compiler/src/onyxrun.c [new file with mode: 0644]
compiler/src/parser.c [new file with mode: 0644]
compiler/src/polymorph.h [new file with mode: 0644]
compiler/src/symres.c [new file with mode: 0644]
compiler/src/types.c [new file with mode: 0644]
compiler/src/utils.c [new file with mode: 0644]
compiler/src/wasm_emit.c [new file with mode: 0644]
compiler/src/wasm_intrinsics.h [new file with mode: 0644]
compiler/src/wasm_output.h [new file with mode: 0644]
compiler/src/wasm_runtime.c [new file with mode: 0644]
compiler/src/wasm_type_table.h [new file with mode: 0644]
include/astnodes.h [deleted file]
include/bh.h [deleted file]
include/doc.h [deleted file]
include/errors.h [deleted file]
include/lex.h [deleted file]
include/onyx_library.h [deleted file]
include/parser.h [deleted file]
include/small_windows.h [deleted file]
include/stb_ds.h [deleted file]
include/types.h [deleted file]
include/utils.h [deleted file]
include/wasm_emit.h [deleted file]
runtime/build.sh [new file with mode: 0755]
runtime/onyx_runtime.c [new file with mode: 0644]
settings.sh [new file with mode: 0644]
shared/include/bh.h [new file with mode: 0644]
shared/include/onyx_library.h [new file with mode: 0644]
shared/include/small_windows.h [new file with mode: 0644]
shared/include/stb_ds.h [new file with mode: 0644]
src/astnodes.c [deleted file]
src/builtins.c [deleted file]
src/checker.c [deleted file]
src/clone.c [deleted file]
src/doc.c [deleted file]
src/entities.c [deleted file]
src/errors.c [deleted file]
src/lex.c [deleted file]
src/onyx.c [deleted file]
src/onyx_runtime.c [deleted file]
src/onyxrun.c [deleted file]
src/parser.c [deleted file]
src/polymorph.h [deleted file]
src/symres.c [deleted file]
src/types.c [deleted file]
src/utils.c [deleted file]
src/wasm_emit.c [deleted file]
src/wasm_intrinsics.h [deleted file]
src/wasm_output.h [deleted file]
src/wasm_runtime.c [deleted file]
src/wasm_type_table.h [deleted file]

index 5f529668d1d54996f16621cbee6febc5da3942c7..7b685b80e5f8e08d1f47aba0ca4fc4023f46cf80 100755 (executable)
--- a/build.sh
+++ b/build.sh
 #!/bin/sh
 
-# The base path for the installation.
-# This is typcially /usr or /usr/local.
-INSTALL_DIR="/usr"
-
-# Where the core libraries for Onyx will go. 
-CORE_DIR="$INSTALL_DIR/share/onyx"
-
-# Where the onyx executable will be placed.
-BIN_DIR="$INSTALL_DIR/bin"
-
-# The compiler to use. Only GCC and TCC have been tested.
-CC='gcc'
-
-# The architecture of your system. If your not sure, leave this alone.
-ARCH="$(uname -m)"
-
-# RUNTIME_LIBRARY="ovmwasm"
-RUNTIME_LIBRARY="wasmer"
-
-# Comment this line if you do not want the above library installed,
-# and do not with to have the Onyx runtime.
-ENABLE_BUNDLING_WASM_RUNTIME=1
-
-# Where the Wasmer library files can be found.
-# They are bundled with the project, but if a different version is available, these can be changed.
-WASMER_INCLUDE_DIR="$(pwd)/lib/common/include"
-WASMER_LIBRARY_DIR="$(pwd)/lib/linux_$ARCH/lib"
-
-# Where the intermediate build files go.
-BUILD_DIR='./build'
-
-# Temporary flag
-ENABLE_DEBUG_INFO=0
-
-
-
+. ./settings.sh
 
 echo "Installing core libs"
 sudo mkdir -p "$CORE_DIR"
 sudo cp -r ./core/ "$CORE_DIR"
 
-# This is a development feature to allow for quickly reinstalling core libraries
-# without have to recompile the entire compiler
-[ "$1" = "core" ] && exit 0
+if [ ! -f "$CORE_DIR/lib/lib$RUNTIME_LIBRARY.so" ] || true; then
+    echo "Copying lib$RUNTIME_LIBRARY to $CORE_DIR/lib (first install)"
 
+    sudo mkdir -p "$CORE_DIR/lib"
+    sudo mkdir -p "$CORE_DIR/include"
 
-C_FILES="onyx astnodes builtins checker clone doc entities errors lex parser symres types utils wasm_emit"
-LIBS=
-INCLUDES="-I./include"
+    sudo cp "$WASMER_LIBRARY_DIR/lib$RUNTIME_LIBRARY.so" "$CORE_DIR/lib/lib$RUNTIME_LIBRARY.so"
 
-WARNINGS='-Wimplicit -Wmisleading-indentation -Wparentheses -Wsequence-point -Wreturn-type -Wshift-negative-value -Wunused-but-set-parameter -Wunused-but-set-variable -Wunused-function -Wunused-label -Wmaybe-uninitialized -Wsign-compare -Wstrict-overflow -Wduplicated-branches -Wduplicated-cond -Wtrigraphs -Waddress -Wlogical-op'
-
-TARGET="./bin/onyx"
-if [ "$1" = "debug" ]; then
-    FLAGS="$WARNINGS -g3"
-else
-    FLAGS="$WARNINGS -O3"
-fi
-
-if [ "$RUNTIME_LIBRARY" = "ovmwasm" ]; then
-    FLAGS="$FLAGS -DUSE_OVM_DEBUGGER"
+    sudo cp "shared/include/onyx_library.h" "$CORE_DIR/include/onyx_library.h"
+    sudo cp "$WASMER_INCLUDE_DIR/wasm.h" "$CORE_DIR/include/wasm.h"
 fi
 
-if [ "$ENABLE_DEBUG_INFO" = "1" ]; then
-    FLAGS="$FLAGS -DENABLE_DEBUG_INFO"
-fi
-
-
-if [ ! -z "$ENABLE_BUNDLING_WASM_RUNTIME" ]; then
-    C_FILES="$C_FILES wasm_runtime"
-    FLAGS="$FLAGS -DENABLE_RUN_WITH_WASMER"
-    LIBS="$LIBS -L$CORE_DIR/lib -l$RUNTIME_LIBRARY -Wl,-rpath=$CORE_DIR/lib:./ -lpthread -ldl -lm"
-    INCLUDES="$INCLUDES -I$WASMER_INCLUDE_DIR"
-
-    if [ ! -f "$CORE_DIR/lib/lib$RUNTIME_LIBRARY.so" ] || true; then
-        echo "Copying lib$RUNTIME_LIBRARY to $CORE_DIR/lib (first install)"
-
-        sudo mkdir -p "$CORE_DIR/lib"
-        sudo mkdir -p "$CORE_DIR/include"
-
-        sudo cp "$WASMER_LIBRARY_DIR/lib$RUNTIME_LIBRARY.so" "$CORE_DIR/lib/lib$RUNTIME_LIBRARY.so"
-
-        sudo cp "include/onyx_library.h" "$CORE_DIR/include/onyx_library.h"
-        sudo cp "lib/common/include/wasm.h" "$CORE_DIR/include/wasm.h"
-    fi
-fi
-
-mkdir -p "$BUILD_DIR"
-
-compile() {
-    for file in $C_FILES ; do
-        echo "Compiling $file.c"
-        $CC -o $BUILD_DIR/$file.o \
-            $FLAGS \
-            "-DCORE_INSTALLATION=\"$CORE_DIR\"" \
-            -c src/$file.c \
-            $INCLUDES $LIBS
-    done
-
-    echo "Linking $TARGET"
-    $CC -o $TARGET $FLAGS $(for file in $C_FILES ; do printf "$BUILD_DIR/%s.o " $file ; done) $LIBS
-
-    echo "Removing object files"
-    for file in $C_FILES ; do rm -f "$BUILD_DIR/$file".o 2>/dev/null ; done
-}
-
-compile
-echo "Installing onyx executable"
-sudo mkdir -p "$BIN_DIR"
-sudo cp ./bin/onyx "$BIN_DIR/onyx"
-
-if [ ! -z "$ENABLE_BUNDLING_WASM_RUNTIME" ]; then
-    C_FILES="onyxrun wasm_runtime"
-    TARGET="./bin/onyx-run"
-
-    compile
-    echo "Installing onyxrun executable"
-    sudo cp ./bin/onyx-run "$BIN_DIR/onyx-run"
+# This is a development feature to allow for quickly reinstalling core libraries
+# without have to recompile the entire compiler
+[ "$1" = "core" ] && exit 0
 
-    $CC -shared -fpic -I include -I lib/common/include src/onyx_runtime.c -o onyx_runtime.so -lpthread
-    sudo mv "./onyx_runtime.so" "$CORE_DIR/lib/onyx_runtime.so"
+sudo cp ./bin/onyx-pkg "$BIN_DIR/onyx-pkg"
+sudo mkdir -p "$CORE_DIR/tools"
+sudo cp ./scripts/onyx-pkg.onyx "$CORE_DIR/tools"
 
+cd compiler
+./build.sh
+cd ..
 
-    sudo cp ./bin/onyx-pkg "$BIN_DIR/onyx-pkg"
-    sudo mkdir -p "$CORE_DIR/tools"
-    sudo cp ./scripts/onyx-pkg.onyx "$CORE_DIR/tools"
-fi
+cd runtime
+./build.sh
+cd ..
 
 # Otherwise the prompt ends on the same line
 printf "\n"
diff --git a/compiler/build.sh b/compiler/build.sh
new file mode 100755 (executable)
index 0000000..2bf9a64
--- /dev/null
@@ -0,0 +1,54 @@
+#!/bin/sh
+
+. ../settings.sh
+
+# Temporary flag
+ENABLE_DEBUG_INFO=1
+
+C_FILES="onyx astnodes builtins checker clone doc entities errors lex parser symres types utils wasm_emit wasm_runtime "
+LIBS="-L$CORE_DIR/lib -l$RUNTIME_LIBRARY -Wl,-rpath=$CORE_DIR/lib:./ -lpthread -ldl -lm"
+INCLUDES="-I./include -I../shared/include -I../lib/common/include"
+
+WARNINGS='-Wimplicit -Wmisleading-indentation -Wparentheses -Wsequence-point -Wreturn-type -Wshift-negative-value -Wunused-but-set-parameter -Wunused-but-set-variable -Wunused-function -Wunused-label -Wmaybe-uninitialized -Wsign-compare -Wstrict-overflow -Wduplicated-branches -Wduplicated-cond -Wtrigraphs -Waddress -Wlogical-op'
+
+if [ "$1" = "debug" ]; then
+    FLAGS="$WARNINGS -g3"
+else
+    FLAGS="$WARNINGS -O3"
+fi
+
+if [ "$RUNTIME_LIBRARY" = "ovmwasm" ]; then
+    FLAGS="$FLAGS -DUSE_OVM_DEBUGGER"
+fi
+
+if [ "$ENABLE_DEBUG_INFO" = "1" ]; then
+    FLAGS="$FLAGS -DENABLE_DEBUG_INFO"
+fi
+
+FLAGS="$FLAGS -DENABLE_RUN_WITH_WASMER"
+
+sudo mkdir -p "$BIN_DIR"
+
+echo "Compiling onyx..."
+$CC -o "../bin/onyx" \
+    $FLAGS \
+    "-DCORE_INSTALLATION=\"$CORE_DIR\"" \
+    $INCLUDES \
+    $(echo "$C_FILES" | sed 's/ /\n/g;s/\([a-zA-Z_0-9]*\)\n/src\/\1.c\n/g;s/\n/ /g') \
+    $LIBS
+
+echo "Installing onyx executable"
+sudo cp "../bin/onyx" "$BIN_DIR/onyx"
+
+C_FILES="onyxrun wasm_runtime "
+
+echo "Compiling onyx-run..."
+$CC -o "../bin/onyx-run" \
+    $FLAGS \
+    "-DCORE_INSTALLATION=\"CORE_DIR\"" \
+    $INCLUDES \
+    $(echo "$C_FILES" | sed 's/ /\n/g;s/\([a-zA-Z_0-9]*\)\n/src\/\1.c\n/g;s/\n/ /g') \
+    $LIBS
+
+echo "Installing onyx-run executable"
+sudo cp "../bin/onyx-run" "$BIN_DIR/onyx-run"
diff --git a/compiler/include/astnodes.h b/compiler/include/astnodes.h
new file mode 100644 (file)
index 0000000..0052d23
--- /dev/null
@@ -0,0 +1,1841 @@
+#ifndef ONYXASTNODES_H
+#define ONYXASTNODES_H
+
+#include "stb_ds.h"
+#include "lex.h"
+#include "types.h"
+#include "errors.h"
+
+#define AST_NODES              \
+    NODE(Node)                 \
+    NODE(Typed)                \
+                               \
+    NODE(NamedValue)           \
+    NODE(BinaryOp)             \
+    NODE(UnaryOp)              \
+    NODE(NumLit)               \
+    NODE(StrLit)               \
+    NODE(Local)                \
+    NODE(Call)                 \
+    NODE(Argument)             \
+    NODE(AddressOf)            \
+    NODE(Dereference)          \
+    NODE(Subscript)            \
+    NODE(FieldAccess)          \
+    NODE(UnaryFieldAccess)     \
+    NODE(SizeOf)               \
+    NODE(AlignOf)              \
+    NODE(FileContents)         \
+    NODE(StructLiteral)        \
+    NODE(ArrayLiteral)         \
+    NODE(RangeLiteral)         \
+    NODE(Compound)             \
+    NODE(IfExpression)         \
+    NODE(DoBlock)              \
+                               \
+    NODE(DirectiveSolidify)    \
+    NODE(DirectiveError)       \
+    NODE(DirectiveAddOverload) \
+    NODE(DirectiveOperator)    \
+    NODE(DirectiveExport)      \
+    NODE(DirectiveDefined)     \
+    NODE(DirectiveInit)        \
+    NODE(DirectiveLibrary)     \
+    NODE(DirectiveRemove)      \
+                               \
+    NODE(Return)               \
+    NODE(Jump)                 \
+    NODE(Use)                  \
+                               \
+    NODE(Block)                \
+    NODE(IfWhile)              \
+    NODE(For)                  \
+    NODE(Defer)                \
+    NODE(SwitchCase)           \
+    NODE(Switch)               \
+                               \
+    NODE(Type)                 \
+    NODE(BasicType)            \
+    NODE(PointerType)          \
+    NODE(FunctionType)         \
+    NODE(ArrayType)            \
+    NODE(SliceType)            \
+    NODE(DynArrType)           \
+    NODE(VarArgType)           \
+    NODE(StructType)           \
+    NODE(StructMember)         \
+    NODE(PolyStructType)       \
+    NODE(PolyStructParam)      \
+    NODE(PolyCallType)         \
+    NODE(EnumType)             \
+    NODE(EnumValue)            \
+    NODE(TypeAlias)            \
+    NODE(TypeRawAlias)         \
+    NODE(CompoundType)         \
+    NODE(TypeOf)               \
+    NODE(DistinctType)         \
+                               \
+    NODE(Binding)              \
+    NODE(Alias)                \
+    NODE(Injection)            \
+    NODE(MemRes)               \
+    NODE(Include)              \
+    NODE(UsePackage)           \
+    NODE(Global)               \
+    NODE(Param)                \
+    NODE(Function)             \
+    NODE(OverloadedFunction)   \
+    NODE(Interface)            \
+    NODE(Constraint)           \
+                               \
+    NODE(PolyParam)            \
+    NODE(PolySolution)         \
+    NODE(SolidifiedFunction)   \
+    NODE(PolyProc)             \
+    NODE(PolyQuery)            \
+                               \
+    NODE(Note)                 \
+    NODE(CallSite)             \
+                               \
+    NODE(CodeBlock)            \
+    NODE(DirectiveInsert)      \
+    NODE(Macro)                \
+                               \
+    NODE(ForeignBlock)         \
+                               \
+    NODE(Package)              \
+                               \
+    NODE(ZeroValue)
+
+#define NODE(name) typedef struct Ast ## name Ast ## name;
+AST_NODES
+#undef NODE
+
+typedef struct Package Package;
+
+typedef struct Scope {
+    u64 id;
+    struct Scope *parent;
+    OnyxFilePos created_at;
+    char* name;
+    Table(AstNode *) symbols;
+} Scope;
+
+
+typedef enum AstKind {
+    Ast_Kind_Error,
+    Ast_Kind_Package,
+    Ast_Kind_Load_File,
+    Ast_Kind_Load_Path,
+    Ast_Kind_Load_All,
+    Ast_Kind_Library_Path,
+    Ast_Kind_Memres,
+
+    Ast_Kind_Binding,
+    Ast_Kind_Alias,
+    Ast_Kind_Injection,
+    Ast_Kind_Function,
+    Ast_Kind_Overloaded_Function,
+    Ast_Kind_Polymorphic_Proc,
+    Ast_Kind_Polymorph_Query,
+    Ast_Kind_Interface,
+    Ast_Kind_Constraint,
+    Ast_Kind_Constraint_Sentinel,
+    Ast_Kind_Block,
+    Ast_Kind_Local,
+    Ast_Kind_Global,
+    Ast_Kind_Symbol,
+
+    Ast_Kind_Unary_Op,
+    Ast_Kind_Binary_Op,
+
+    Ast_Kind_Compound,
+    Ast_Kind_Named_Value,
+
+    Ast_Kind_Type_Start,
+    Ast_Kind_Type,
+    Ast_Kind_Basic_Type,
+    Ast_Kind_Pointer_Type,
+    Ast_Kind_Function_Type,
+    Ast_Kind_Array_Type,
+    Ast_Kind_Slice_Type,
+    Ast_Kind_DynArr_Type,
+    Ast_Kind_VarArg_Type,
+    Ast_Kind_Struct_Type,
+    Ast_Kind_Poly_Struct_Type,
+    Ast_Kind_Poly_Call_Type,
+    Ast_Kind_Enum_Type,
+    Ast_Kind_Type_Alias,
+    Ast_Kind_Type_Raw_Alias,
+    Ast_Kind_Type_Compound,
+    Ast_Kind_Typeof,
+    Ast_Kind_Distinct_Type,
+    Ast_Kind_Type_End,
+
+    Ast_Kind_Struct_Member,
+    Ast_Kind_Enum_Value,
+
+    Ast_Kind_NumLit,
+    Ast_Kind_StrLit,
+    Ast_Kind_Param,
+    Ast_Kind_Argument,
+    Ast_Kind_Call,
+    Ast_Kind_Intrinsic_Call,
+    Ast_Kind_Return,
+    Ast_Kind_Address_Of,
+    Ast_Kind_Dereference,
+    Ast_Kind_Subscript,
+    Ast_Kind_Slice,
+    Ast_Kind_Field_Access,
+    Ast_Kind_Unary_Field_Access,
+    Ast_Kind_Pipe,
+    Ast_Kind_Method_Call,
+    Ast_Kind_Range_Literal,
+    Ast_Kind_Size_Of,
+    Ast_Kind_Align_Of,
+    Ast_Kind_File_Contents,
+    Ast_Kind_Struct_Literal,
+    Ast_Kind_Array_Literal,
+    Ast_Kind_If_Expression,
+
+    Ast_Kind_If,
+    Ast_Kind_For,
+    Ast_Kind_While,
+    Ast_Kind_Jump,
+    Ast_Kind_Use,
+    Ast_Kind_Defer,
+    Ast_Kind_Switch,
+    Ast_Kind_Switch_Case,
+
+    Ast_Kind_Directive_Solidify,
+    Ast_Kind_Static_If,
+    Ast_Kind_Directive_Error,
+    Ast_Kind_Directive_Add_Overload,
+    Ast_Kind_Directive_Operator,
+    Ast_Kind_Directive_Export,
+    Ast_Kind_Directive_Defined,
+    Ast_Kind_Directive_Init,
+    Ast_Kind_Directive_Library,
+    Ast_Kind_Directive_Remove,
+    Ast_Kind_Call_Site,
+
+    Ast_Kind_Code_Block,
+    Ast_Kind_Directive_Insert,
+    Ast_Kind_Macro,
+    Ast_Kind_Do_Block,
+
+    Ast_Kind_Foreign_Block,
+
+    Ast_Kind_Zero_Value,
+
+    Ast_Kind_Note,
+
+    Ast_Kind_Count
+} AstKind;
+
+// NOTE: Some of these flags will overlap since there are
+// only 32-bits of flags to play with
+typedef enum AstFlags {
+    // Top-level flags
+    Ast_Flag_Const                 = BH_BIT(1),
+    Ast_Flag_Comptime              = BH_BIT(2),
+    Ast_Flag_Private_Package       = BH_BIT(3),
+    Ast_Flag_Private_File          = BH_BIT(4),
+
+    // Function flags
+    Ast_Flag_Function_Used         = BH_BIT(5),
+
+    // Expression flags
+    Ast_Flag_Expr_Ignored          = BH_BIT(6),
+    Ast_Flag_Address_Taken         = BH_BIT(7),
+
+    // Type flags
+    Ast_Flag_Type_Is_Resolved      = BH_BIT(8),
+
+    Ast_Flag_No_Clone              = BH_BIT(9),
+
+    Ast_Flag_Cannot_Take_Addr      = BH_BIT(10),
+
+    // HACK: NullProcHack
+    Ast_Flag_Proc_Is_Null          = BH_BIT(11),
+
+    Ast_Flag_From_Polymorphism     = BH_BIT(12),
+
+    Ast_Flag_Incomplete_Body       = BH_BIT(13),
+
+    Ast_Flag_Array_Literal_Typed   = BH_BIT(14),
+
+    Ast_Flag_Has_Been_Symres       = BH_BIT(15),
+
+    Ast_Flag_Has_Been_Checked      = BH_BIT(16),
+
+    Ast_Flag_Static_If_Resolved    = BH_BIT(17),
+
+    Ast_Flag_Symbol_Invisible      = BH_BIT(18),
+
+    Ast_Flag_Header_Check_No_Error = BH_BIT(19),
+
+    Ast_Flag_Decl_Followed_By_Init = BH_BIT(20),
+
+    Ast_Flag_Param_Symbol_Dirty    = BH_BIT(21),
+
+    Ast_Flag_Dead                  = BH_BIT(22),
+} AstFlags;
+
+typedef enum UnaryOp {
+    Unary_Op_Negate,
+    Unary_Op_Not,
+    Unary_Op_Bitwise_Not,
+    Unary_Op_Cast,
+    Unary_Op_Auto_Cast,
+} UnaryOp;
+
+typedef enum BinaryOp {
+    Binary_Op_Add              = 0,
+    Binary_Op_Minus            = 1,
+    Binary_Op_Multiply         = 2,
+    Binary_Op_Divide           = 3,
+    Binary_Op_Modulus          = 4,
+
+    Binary_Op_Equal            = 5,
+    Binary_Op_Not_Equal        = 6,
+    Binary_Op_Less             = 7,
+    Binary_Op_Less_Equal       = 8,
+    Binary_Op_Greater          = 9,
+    Binary_Op_Greater_Equal    = 10,
+
+    Binary_Op_And              = 11,
+    Binary_Op_Or               = 12,
+    Binary_Op_Xor              = 13,
+    Binary_Op_Shl              = 14,
+    Binary_Op_Shr              = 15,
+    Binary_Op_Sar              = 16,
+
+    Binary_Op_Bool_And         = 17,
+    Binary_Op_Bool_Or          = 18,
+
+    Binary_Op_Assign_Start     = 19,
+    Binary_Op_Assign           = 20,
+    Binary_Op_Assign_Add       = 21,
+    Binary_Op_Assign_Minus     = 22,
+    Binary_Op_Assign_Multiply  = 23,
+    Binary_Op_Assign_Divide    = 24,
+    Binary_Op_Assign_Modulus   = 25,
+    Binary_Op_Assign_And       = 26,
+    Binary_Op_Assign_Or        = 27,
+    Binary_Op_Assign_Xor       = 28,
+    Binary_Op_Assign_Shl       = 29,
+    Binary_Op_Assign_Shr       = 30,
+    Binary_Op_Assign_Sar       = 31,
+    Binary_Op_Assign_End       = 32,
+
+    Binary_Op_Pipe             = 33,
+    Binary_Op_Range            = 34,
+    Binary_Op_Method_Call      = 35,
+
+    Binary_Op_Subscript        = 36,
+    Binary_Op_Subscript_Equals = 37,
+    Binary_Op_Ptr_Subscript    = 38,
+
+    Binary_Op_Count
+} BinaryOp;
+
+extern const char *binaryop_string[Binary_Op_Count];
+
+typedef enum OnyxIntrinsic {
+    ONYX_INTRINSIC_UNDEFINED,
+
+    ONYX_INTRINSIC_MEMORY_SIZE, ONYX_INTRINSIC_MEMORY_GROW,
+    ONYX_INTRINSIC_MEMORY_COPY, ONYX_INTRINSIC_MEMORY_FILL,
+
+    ONYX_INTRINSIC_INITIALIZE,
+
+    ONYX_INTRINSIC_I32_CLZ,   ONYX_INTRINSIC_I32_CTZ, ONYX_INTRINSIC_I32_POPCNT,
+    ONYX_INTRINSIC_I32_AND,   ONYX_INTRINSIC_I32_OR,  ONYX_INTRINSIC_I32_XOR,
+    ONYX_INTRINSIC_I32_SHL,   ONYX_INTRINSIC_I32_SLR, ONYX_INTRINSIC_I32_SAR,
+    ONYX_INTRINSIC_I32_ROTL,  ONYX_INTRINSIC_I32_ROTR,
+
+    ONYX_INTRINSIC_I64_CLZ,   ONYX_INTRINSIC_I64_CTZ, ONYX_INTRINSIC_I64_POPCNT,
+    ONYX_INTRINSIC_I64_AND,   ONYX_INTRINSIC_I64_OR,  ONYX_INTRINSIC_I64_XOR,
+    ONYX_INTRINSIC_I64_SHL,   ONYX_INTRINSIC_I64_SLR, ONYX_INTRINSIC_I64_SAR,
+    ONYX_INTRINSIC_I64_ROTL,  ONYX_INTRINSIC_I64_ROTR,
+
+    ONYX_INTRINSIC_F32_ABS,   ONYX_INTRINSIC_F32_SQRT,
+    ONYX_INTRINSIC_F32_CEIL,  ONYX_INTRINSIC_F32_FLOOR,
+    ONYX_INTRINSIC_F32_TRUNC, ONYX_INTRINSIC_F32_NEAREST,
+    ONYX_INTRINSIC_F32_MIN,   ONYX_INTRINSIC_F32_MAX,
+    ONYX_INTRINSIC_F32_COPYSIGN,
+
+    ONYX_INTRINSIC_F64_ABS,   ONYX_INTRINSIC_F64_SQRT,
+    ONYX_INTRINSIC_F64_CEIL,  ONYX_INTRINSIC_F64_FLOOR,
+    ONYX_INTRINSIC_F64_TRUNC, ONYX_INTRINSIC_F64_NEAREST,
+    ONYX_INTRINSIC_F64_MIN,   ONYX_INTRINSIC_F64_MAX,
+    ONYX_INTRINSIC_F64_COPYSIGN,
+
+
+
+    ONYX_INTRINSIC_V128_CONST,
+    ONYX_INTRINSIC_I8X16_CONST, ONYX_INTRINSIC_I16X8_CONST,
+    ONYX_INTRINSIC_I32X4_CONST, ONYX_INTRINSIC_I64X2_CONST,
+    ONYX_INTRINSIC_F32X4_CONST, ONYX_INTRINSIC_F64X2_CONST,
+    ONYX_INTRINSIC_I8X16_SHUFFLE,
+
+    ONYX_INTRINSIC_I8X16_EXTRACT_LANE_S, ONYX_INTRINSIC_I8X16_EXTRACT_LANE_U, ONYX_INTRINSIC_I8X16_REPLACE_LANE,
+    ONYX_INTRINSIC_I16X8_EXTRACT_LANE_S, ONYX_INTRINSIC_I16X8_EXTRACT_LANE_U, ONYX_INTRINSIC_I16X8_REPLACE_LANE,
+    ONYX_INTRINSIC_I32X4_EXTRACT_LANE, ONYX_INTRINSIC_I32X4_REPLACE_LANE,
+    ONYX_INTRINSIC_I64X2_EXTRACT_LANE, ONYX_INTRINSIC_I64X2_REPLACE_LANE,
+    ONYX_INTRINSIC_F32X4_EXTRACT_LANE, ONYX_INTRINSIC_F32X4_REPLACE_LANE,
+    ONYX_INTRINSIC_F64X2_EXTRACT_LANE, ONYX_INTRINSIC_F64X2_REPLACE_LANE,
+
+    ONYX_INTRINSIC_I8X16_SWIZZLE,
+    ONYX_INTRINSIC_I8X16_SPLAT, ONYX_INTRINSIC_I16X8_SPLAT,
+    ONYX_INTRINSIC_I32X4_SPLAT, ONYX_INTRINSIC_I64X2_SPLAT,
+    ONYX_INTRINSIC_F32X4_SPLAT, ONYX_INTRINSIC_F64X2_SPLAT,
+
+    ONYX_INTRINSIC_I8X16_EQ, ONYX_INTRINSIC_I8X16_NEQ,
+    ONYX_INTRINSIC_I8X16_LT_S, ONYX_INTRINSIC_I8X16_LT_U,
+    ONYX_INTRINSIC_I8X16_GT_S, ONYX_INTRINSIC_I8X16_GT_U,
+    ONYX_INTRINSIC_I8X16_LE_S, ONYX_INTRINSIC_I8X16_LE_U,
+    ONYX_INTRINSIC_I8X16_GE_S, ONYX_INTRINSIC_I8X16_GE_U,
+
+    ONYX_INTRINSIC_I16X8_EQ, ONYX_INTRINSIC_I16X8_NEQ,
+    ONYX_INTRINSIC_I16X8_LT_S, ONYX_INTRINSIC_I16X8_LT_U,
+    ONYX_INTRINSIC_I16X8_GT_S, ONYX_INTRINSIC_I16X8_GT_U,
+    ONYX_INTRINSIC_I16X8_LE_S, ONYX_INTRINSIC_I16X8_LE_U,
+    ONYX_INTRINSIC_I16X8_GE_S, ONYX_INTRINSIC_I16X8_GE_U,
+
+    ONYX_INTRINSIC_I32X4_EQ, ONYX_INTRINSIC_I32X4_NEQ,
+    ONYX_INTRINSIC_I32X4_LT_S, ONYX_INTRINSIC_I32X4_LT_U,
+    ONYX_INTRINSIC_I32X4_GT_S, ONYX_INTRINSIC_I32X4_GT_U,
+    ONYX_INTRINSIC_I32X4_LE_S, ONYX_INTRINSIC_I32X4_LE_U,
+    ONYX_INTRINSIC_I32X4_GE_S, ONYX_INTRINSIC_I32X4_GE_U,
+
+    ONYX_INTRINSIC_F32X4_EQ, ONYX_INTRINSIC_F32X4_NEQ,
+    ONYX_INTRINSIC_F32X4_LT, ONYX_INTRINSIC_F32X4_GT,
+    ONYX_INTRINSIC_F32X4_LE, ONYX_INTRINSIC_F32X4_GE,
+
+    ONYX_INTRINSIC_F64X2_EQ, ONYX_INTRINSIC_F64X2_NEQ,
+    ONYX_INTRINSIC_F64X2_LT, ONYX_INTRINSIC_F64X2_GT,
+    ONYX_INTRINSIC_F64X2_LE, ONYX_INTRINSIC_F64X2_GE,
+
+    ONYX_INTRINSIC_V128_NOT, ONYX_INTRINSIC_V128_AND, ONYX_INTRINSIC_V128_ANDNOT,
+    ONYX_INTRINSIC_V128_OR, ONYX_INTRINSIC_V128_XOR, ONYX_INTRINSIC_V128_BITSELECT,
+
+    ONYX_INTRINSIC_I8X16_ABS, ONYX_INTRINSIC_I8X16_NEG,
+    ONYX_INTRINSIC_I8X16_ANY_TRUE, ONYX_INTRINSIC_I8X16_ALL_TRUE,
+    ONYX_INTRINSIC_I8X16_BITMASK,
+    ONYX_INTRINSIC_I8X16_NARROW_I16X8_S, ONYX_INTRINSIC_I8X16_NARROW_I16X8_U,
+    ONYX_INTRINSIC_I8X16_SHL, ONYX_INTRINSIC_I8X16_SHR_S, ONYX_INTRINSIC_I8X16_SHR_U,
+    ONYX_INTRINSIC_I8X16_ADD, ONYX_INTRINSIC_I8X16_ADD_SAT_S, ONYX_INTRINSIC_I8X16_ADD_SAT_U,
+    ONYX_INTRINSIC_I8X16_SUB, ONYX_INTRINSIC_I8X16_SUB_SAT_S, ONYX_INTRINSIC_I8X16_SUB_SAT_U,
+    ONYX_INTRINSIC_I8X16_MIN_S, ONYX_INTRINSIC_I8X16_MIN_U,
+    ONYX_INTRINSIC_I8X16_MAX_S, ONYX_INTRINSIC_I8X16_MAX_U,
+    ONYX_INTRINSIC_I8X16_AVGR_U,
+
+    ONYX_INTRINSIC_I16X8_ABS, ONYX_INTRINSIC_I16X8_NEG,
+    ONYX_INTRINSIC_I16X8_ANY_TRUE, ONYX_INTRINSIC_I16X8_ALL_TRUE,
+    ONYX_INTRINSIC_I16X8_BITMASK,
+    ONYX_INTRINSIC_I16X8_NARROW_I32X4_S, ONYX_INTRINSIC_I16X8_NARROW_I32X4_U,
+    ONYX_INTRINSIC_I16X8_WIDEN_LOW_I8X16_S, ONYX_INTRINSIC_I16X8_WIDEN_HIGH_I8X16_S,
+    ONYX_INTRINSIC_I16X8_WIDEN_LOW_I8X16_U, ONYX_INTRINSIC_I16X8_WIDEN_HIGH_I8X16_U,
+    ONYX_INTRINSIC_I16X8_SHL, ONYX_INTRINSIC_I16X8_SHR_S, ONYX_INTRINSIC_I16X8_SHR_U,
+    ONYX_INTRINSIC_I16X8_ADD, ONYX_INTRINSIC_I16X8_ADD_SAT_S, ONYX_INTRINSIC_I16X8_ADD_SAT_U,
+    ONYX_INTRINSIC_I16X8_SUB, ONYX_INTRINSIC_I16X8_SUB_SAT_S, ONYX_INTRINSIC_I16X8_SUB_SAT_U,
+    ONYX_INTRINSIC_I16X8_MUL,
+    ONYX_INTRINSIC_I16X8_MIN_S, ONYX_INTRINSIC_I16X8_MIN_U,
+    ONYX_INTRINSIC_I16X8_MAX_S, ONYX_INTRINSIC_I16X8_MAX_U,
+    ONYX_INTRINSIC_I16X8_AVGR_U,
+
+    ONYX_INTRINSIC_I32X4_ABS, ONYX_INTRINSIC_I32X4_NEG,
+    ONYX_INTRINSIC_I32X4_ANY_TRUE, ONYX_INTRINSIC_I32X4_ALL_TRUE,
+    ONYX_INTRINSIC_I32X4_BITMASK,
+    ONYX_INTRINSIC_I32X4_WIDEN_LOW_I16X8_S, ONYX_INTRINSIC_I32X4_WIDEN_HIGH_I16X8_S,
+    ONYX_INTRINSIC_I32X4_WIDEN_LOW_I16X8_U, ONYX_INTRINSIC_I32X4_WIDEN_HIGH_I16X8_U,
+    ONYX_INTRINSIC_I32X4_SHL, ONYX_INTRINSIC_I32X4_SHR_S, ONYX_INTRINSIC_I32X4_SHR_U,
+    ONYX_INTRINSIC_I32X4_ADD, ONYX_INTRINSIC_I32X4_SUB, ONYX_INTRINSIC_I32X4_MUL,
+    ONYX_INTRINSIC_I32X4_MIN_S, ONYX_INTRINSIC_I32X4_MIN_U,
+    ONYX_INTRINSIC_I32X4_MAX_S, ONYX_INTRINSIC_I32X4_MAX_U,
+
+    ONYX_INTRINSIC_I64X2_NEG, ONYX_INTRINSIC_I64X2_SHL,
+    ONYX_INTRINSIC_I64X2_SHR_S, ONYX_INTRINSIC_I64X2_SHR_U,
+    ONYX_INTRINSIC_I64X2_ADD, ONYX_INTRINSIC_I64X2_SUB, ONYX_INTRINSIC_I64X2_MUL,
+
+    ONYX_INTRINSIC_F32X4_ABS, ONYX_INTRINSIC_F32X4_NEG, ONYX_INTRINSIC_F32X4_SQRT,
+    ONYX_INTRINSIC_F32X4_ADD, ONYX_INTRINSIC_F32X4_SUB,
+    ONYX_INTRINSIC_F32X4_MUL, ONYX_INTRINSIC_F32X4_DIV,
+    ONYX_INTRINSIC_F32X4_MIN, ONYX_INTRINSIC_F32X4_MAX,
+
+    ONYX_INTRINSIC_F64X2_ABS, ONYX_INTRINSIC_F64X2_NEG, ONYX_INTRINSIC_F64X2_SQRT,
+    ONYX_INTRINSIC_F64X2_ADD, ONYX_INTRINSIC_F64X2_SUB,
+    ONYX_INTRINSIC_F64X2_MUL, ONYX_INTRINSIC_F64X2_DIV,
+    ONYX_INTRINSIC_F64X2_MIN, ONYX_INTRINSIC_F64X2_MAX,
+
+    ONYX_INTRINSIC_I32X4_TRUNC_SAT_F32X4_S,
+    ONYX_INTRINSIC_I32X4_TRUNC_SAT_F32X4_U,
+    ONYX_INTRINSIC_F32X4_CONVERT_I32X4_S,
+    ONYX_INTRINSIC_F32X4_CONVERT_I32X4_U,
+
+    ONYX_INTRINSIC_ATOMIC_WAIT,
+    ONYX_INTRINSIC_ATOMIC_NOTIFY,
+
+    ONYX_INTRINSIC_ATOMIC_FENCE,
+
+    ONYX_INTRINSIC_ATOMIC_LOAD,
+    ONYX_INTRINSIC_ATOMIC_STORE,
+    ONYX_INTRINSIC_ATOMIC_ADD,
+    ONYX_INTRINSIC_ATOMIC_SUB,
+    ONYX_INTRINSIC_ATOMIC_AND,
+    ONYX_INTRINSIC_ATOMIC_OR,
+    ONYX_INTRINSIC_ATOMIC_XOR,
+    ONYX_INTRINSIC_ATOMIC_XCHG,
+    ONYX_INTRINSIC_ATOMIC_CMPXCHG,
+} OnyxIntrinsic;
+
+typedef enum CallingConvention {
+    CC_Undefined,
+    CC_Return_Wasm,
+    CC_Return_Stack
+} CallingConvention;
+
+typedef enum JumpType {
+    Jump_Type_Break,
+    Jump_Type_Continue,
+    Jump_Type_Fallthrough,
+    Jump_Type_Return,
+
+    Jump_Type_Count,
+} JumpType;
+
+typedef enum ForLoopType {
+    For_Loop_Invalid,
+    For_Loop_Range,
+    For_Loop_Array,
+    For_Loop_Slice,
+    For_Loop_DynArr,
+    For_Loop_Iterator,
+} ForLoopType;
+
+typedef enum ParamPassType {
+    Param_Pass_Invalid,
+    Param_Pass_By_Value,
+    Param_Pass_By_VarArg,
+    Param_Pass_By_Implicit_Pointer,
+} ParamPassType;
+
+typedef enum VarArgKind {
+    VA_Kind_Not_VA,
+    VA_Kind_Typed,
+    VA_Kind_Any,
+    VA_Kind_Untyped,
+} VarArgKind;
+
+typedef enum BlockRule {
+    Block_Rule_New_Scope         = BH_BIT(1),
+    Block_Rule_Emit_Instructions = BH_BIT(2),
+    Block_Rule_Clear_Defer       = BH_BIT(3),
+    Block_Rule_Override_Return   = BH_BIT(4),
+
+    Block_Rule_Normal     = Block_Rule_New_Scope | Block_Rule_Clear_Defer | Block_Rule_Emit_Instructions,
+    Block_Rule_Macro      = Block_Rule_New_Scope,
+    Block_Rule_Code_Block = Block_Rule_New_Scope,
+    Block_Rule_Do_Block   = Block_Rule_New_Scope | Block_Rule_Clear_Defer | Block_Rule_Override_Return | Block_Rule_Emit_Instructions,
+} BlockRule;
+
+typedef enum ConstraintCheckStatus {
+    Constraint_Check_Status_Queued,
+    Constraint_Check_Status_Failed,
+    Constraint_Check_Status_Success,
+} ConstraintCheckStatus;
+
+typedef struct ConstraintContext {
+    bh_arr(AstConstraint *) constraints;
+    ConstraintCheckStatus *constraint_checks;
+    b32 constraints_met : 1;
+    b32 produce_errors  : 1;
+} ConstraintContext;
+
+
+typedef struct Arguments Arguments;
+struct Arguments {
+    bh_arr(AstTyped *) values;
+    bh_arr(AstNamedValue *) named_values;
+
+    // How many arguments were not baked.
+    i32 used_argument_count;
+};
+
+
+// Base Nodes
+#define AstNode_base \
+    AstKind kind;             \
+    u32 flags;                \
+    OnyxToken *token;         \
+    struct Entity* entity;    \
+    AstNode *next
+struct AstNode { AstNode_base; };
+
+// NOTE: 'type_node' is filled out by the parser.
+// For a type such as '^^i32', the tree would look something like
+//
+//      Typed Thing -> AstPointerType -> AstPointerType -> AstNode (symbol node)
+//
+// The symbol node will be filled out during symbol resolution.
+// It will end up pointing to an AstBasicType that corresponds to
+// the underlying type.
+//
+// 'type' is filled out afterwards. If it is NULL, the Type* is built
+// using the type_node. This can then be used to typecheck this node.
+#define AstTyped_base       \
+    AstNode_base;           \
+    AstType *type_node;     \
+    Type *type
+struct AstTyped { AstTyped_base; };
+
+// Expression Nodes
+struct AstNamedValue    { AstTyped_base; AstTyped* value; };
+struct AstUnaryOp       { AstTyped_base; UnaryOp operation; AstTyped *expr; };
+struct AstStrLit        { AstTyped_base; u64 data_id; u64 length; b32 is_cstr: 1; };
+struct AstLocal         { AstTyped_base; };
+struct AstDereference   { AstTyped_base; AstTyped *expr; };
+struct AstSizeOf        { AstTyped_base; AstType *so_ast_type; Type *so_type; u64 size; };
+struct AstAlignOf       { AstTyped_base; AstType *ao_ast_type; Type *ao_type; u64 alignment; };
+struct AstNumLit        {
+    AstTyped_base;
+    union {
+        i32 i;
+        i64 l;
+        f32 f;
+        f64 d;
+    } value;
+
+    b32 was_hex_literal : 1;
+};
+struct AstBinaryOp      {
+    AstTyped_base;
+    BinaryOp operation;
+    AstTyped *left, *right;
+
+    Arguments *overload_args; // This is set of the binary operator is attempted to be overloaded
+                              // but isnt successful yet.
+    AstBinaryOp *potential_substitute;
+};
+struct AstAddressOf     {
+    AstTyped_base;
+    AstTyped *expr;
+
+    AstBinaryOp *potential_substitute;
+
+    // This is set by check_method_call.
+    // If set, the address of node can be removed if the
+    // type checking does not pass for it. This makes it
+    // possible to have something that will optionally
+    // have its address taken, if necessary.
+    b32 can_be_removed : 1;
+};
+struct AstArgument      {
+    AstTyped_base;
+
+    AstTyped *value;
+
+    VarArgKind va_kind;
+    b32 is_baked : 1;
+    b32 pass_as_any : 1;
+};
+struct AstSubscript   {
+    AstTyped_base;
+    BinaryOp __unused_operation; // This will be set to Binary_Op_Subscript
+    AstTyped *addr;
+    AstTyped *expr;
+
+    Arguments *overload_args; // This is set of the binary operator is attempted to be overloaded
+                              // but isnt successful yet.
+    AstBinaryOp *potential_substitute;
+
+    u64 elem_size;
+};
+struct AstFieldAccess   {
+    AstTyped_base;
+    AstTyped *expr;
+    u32 offset;
+    u32 idx;
+    char* field; // If token is null, defer to field
+};
+struct AstFileContents  {
+    AstTyped_base;
+
+    AstTyped *filename_expr;
+    char *filename; // The parsed file name, with '\' sequences removed and resolved to a particular file if possible.
+
+    u32 data_id, size;
+};
+struct AstUnaryFieldAccess {
+    AstTyped_base;
+
+    // "token" represents the field. This does not need an "offset" or an "index"
+    // because this node is meant to be replaced.
+};
+struct AstStructLiteral {
+    AstTyped_base;
+
+    AstTyped *stnode;
+
+    Arguments args;
+};
+struct AstArrayLiteral {
+    AstTyped_base;
+
+    AstTyped *atnode;
+
+    bh_arr(AstTyped *) values;
+};
+struct AstRangeLiteral {
+    AstTyped_base; 
+
+    // HACK: Currently, range literals are parsed as binary operators, which means
+    // the first sizeof(AstBinaryOp) bytes of this structure must match that of
+    // AstBinaryOp, which means I need this dummy field here.
+    //                                              - brendanfh 2020/12/23
+    BinaryOp __unused_operation;
+    AstTyped *low, *high;
+
+    // Currently, there is no way to specify this in the grammar, but it is set
+    // to be the initial value of the `step` member of the range structure in
+    // core/builtin.onyx.
+    //                                              - brendanfh 2020/12/23
+    AstTyped *step;
+};
+struct AstCall {
+    AstTyped_base;
+
+    Arguments args;
+
+    union {
+        AstTyped *callee;
+        OnyxIntrinsic intrinsic;
+    };
+
+    VarArgKind va_kind;
+};
+struct AstCompound {
+    AstTyped_base;
+
+    bh_arr(AstTyped *) exprs;
+};
+struct AstIfExpression {
+    AstTyped_base;
+
+    AstTyped* cond;
+    AstTyped* true_expr;
+    AstTyped* false_expr;
+};
+struct AstDoBlock {
+    AstTyped_base;
+
+    AstBlock* block;
+};
+struct AstZeroValue {
+    AstTyped_base;
+};
+
+struct AstDirectiveSolidify {
+    AstTyped_base;
+
+    AstFunction* poly_proc;
+    bh_arr(AstPolySolution) known_polyvars;
+
+    AstNode* resolved_proc;
+};
+
+// Intruction Node
+struct AstReturn        { AstNode_base; AstTyped* expr; };
+struct AstJump          { AstNode_base; JumpType jump; u32 count; };
+
+typedef struct QualifiedUse {
+    OnyxToken* symbol_name;
+    OnyxToken* as_name;
+} QualifiedUse;
+struct AstUse           {
+    AstNode_base;
+
+    AstTyped* expr;
+    bh_arr(QualifiedUse) only;
+};
+
+// Structure Nodes
+struct AstBlock         {
+    AstNode_base;
+
+    AstNode *body;
+
+    Scope *scope;
+    Scope *binding_scope;
+    BlockRule rules;
+
+    u32 statement_idx;
+};
+struct AstDefer         { AstNode_base; AstNode *stmt; };
+struct AstFor           {
+    AstNode_base;
+
+    // NOTE: Stores the iteration variable
+    Scope *scope;
+
+    // NOTE: Local defining the iteration variable
+    AstLocal* var;
+
+    // NOTE: This can be any expression, but it is checked that
+    // it is of a type that we know how to iterate over.
+    AstTyped* iter;
+    ForLoopType loop_type;
+
+    AstBlock *stmt;
+
+    // ROBUSTNESS: This should be able to be set by a compile time variable at some point.
+    // But for now, this will do.
+    b32 by_pointer : 1;
+    b32 no_close   : 1;
+};
+struct AstIfWhile {
+    AstNode_base;
+
+    Scope *scope;
+    AstNode* initialization;
+
+    AstTyped *cond;
+
+    AstBlock *true_stmt;
+    AstBlock *false_stmt;
+
+    union {
+        // Used by Static_If
+        struct {
+            Scope *defined_in_scope;
+            bh_arr(struct Entity *) true_entities;
+            bh_arr(struct Entity *) false_entities;
+        };
+
+        // Used by While
+        b32 bottom_test;
+    };
+};
+typedef struct AstIfWhile AstIf;
+typedef struct AstIfWhile AstWhile;
+
+typedef enum SwitchKind {
+    Switch_Kind_Integer,
+    Switch_Kind_Use_Equals,
+} SwitchKind;
+
+typedef struct CaseToBlock {
+    AstTyped *original_value;
+    AstBinaryOp *comparison;
+    AstBlock *block;
+} CaseToBlock;
+
+struct AstSwitchCase {
+    AstNode_base;
+
+    // NOTE: All expressions that end up in this block
+    bh_arr(AstTyped *) values;
+    
+    AstBlock *block;
+
+    b32 is_default: 1; // Could this be inferred by the values array being null?
+};
+
+struct AstSwitch {
+    AstNode_base;
+
+    Scope *scope;
+    AstNode* initialization;
+
+    AstTyped *expr;
+
+    AstBlock *case_block;
+
+    bh_arr(AstSwitchCase *) cases;
+    AstBlock *default_case;
+
+    i32 yield_return_index;
+    SwitchKind switch_kind;
+
+    union {
+        struct {
+            // NOTE: This is a mapping from the compile time known case value
+            // to a pointer to the block that it is associated with.
+            bh_imap case_map;
+            u64 min_case, max_case;
+        };
+
+        struct {
+            // NOTE: This is a mapping from the '==' binary op node to
+            // a pointer to the block that it is associated with.
+            bh_arr(CaseToBlock) case_exprs;
+        };
+    };
+};
+
+// Type Nodes
+// NOTE: This node is very similar to an AstNode, just
+// without the 'next' member. This is because types
+// can't be in expressions so a 'next' thing
+// doesn't make sense.
+#define AstType_base       \
+    AstKind kind;          \
+    u32 flags;             \
+    OnyxToken* token;      \
+    struct Entity* entity; \
+    void* __unused;        \
+    u64 type_id;           \
+    Type* type
+struct AstType { AstType_base; };
+
+struct AstBasicType     { AstType_base; Type* basic_type; };
+struct AstPointerType   { AstType_base; AstType* elem; };
+struct AstArrayType     { AstType_base; AstType* elem; AstTyped *count_expr; };
+struct AstSliceType     { AstType_base; AstType* elem; };
+struct AstDynArrType    { AstType_base; AstType* elem; };
+struct AstVarArgType    { AstType_base; AstType* elem; };
+struct AstFunctionType  {
+    AstType_base;
+
+    // Used in a rare case in solve_for_polymorphic_param_type.
+    Type *partial_function_type;
+
+    AstType* return_type;
+
+    u64 param_count;
+    AstType* params[];
+};
+struct AstStructType {
+    AstType_base;
+    char *name;
+
+    bh_arr(AstStructMember *) members;
+    bh_arr(AstTyped *) meta_tags;
+
+    // u32 min_alignment, min_size;
+    AstTyped *min_alignment_, *min_size_;
+
+    // NOTE: Used to cache the actual type, since building
+    // a struct type is kind of complicated and should
+    // only happen once.
+    Type *stcache;
+
+    // NOTE: This type is used when the structure has not been
+    // completely generated, but is a valid pointer to where the
+    // type will be generated to.
+    Type *pending_type;
+    
+    // NOTE: Used to store statically bound expressions in the struct.
+    Scope* scope;
+
+    struct Entity* entity_type;
+    struct Entity* entity_defaults;
+
+    OnyxFilePos polymorphic_error_loc;
+    ConstraintContext constraints;
+
+    bh_arr(AstType *)       polymorphic_argument_types;
+    bh_arr(AstPolySolution) polymorphic_arguments;
+
+    b32 pending_type_is_valid : 1;
+    b32 is_union              : 1;
+    b32 is_packed             : 1;
+    b32 ready_to_build_type   : 1;
+};
+struct AstStructMember {
+    AstTyped_base;
+    AstTyped* initial_value;
+
+    bh_arr(AstTyped *) meta_tags;
+
+    b32 is_used : 1;
+};
+struct AstPolyStructParam {
+    AstTyped_base;
+};
+struct AstPolyStructType {
+    AstType_base;
+    char *name;
+
+    Scope *scope;
+    bh_arr(AstPolyStructParam) poly_params;
+    Table(AstStructType *) concrete_structs;
+
+    AstStructType* base_struct;
+};
+struct AstPolyCallType {
+    AstType_base;
+
+    AstType* callee;
+
+    // NOTE: These nodes can be either AstTypes, or AstTyped expressions.
+    bh_arr(AstNode *) params;
+};
+struct AstEnumType {
+    AstType_base;
+    char *name;
+    Scope *scope;
+
+    AstType *backing;
+    Type    *backing_type;
+
+    bh_arr(AstEnumValue *) values;
+
+    // NOTE: Used to cache the actual type for the same reason as above.
+    Type *etcache;
+
+    b32 is_flags : 1;
+};
+struct AstEnumValue    { AstTyped_base; AstTyped* value; };
+struct AstTypeAlias    { AstType_base; AstType* to; };
+struct AstTypeRawAlias { AstType_base; Type* to; };
+struct AstCompoundType {
+    AstType_base;
+
+    bh_arr(AstType *) types;
+};
+struct AstTypeOf {
+    AstType_base;
+
+    AstTyped* expr;
+    Type* resolved_type;
+};
+struct AstDistinctType {
+    AstType_base;
+    char *name;
+    AstType *base_type;
+    Type *dtcache;
+};
+
+// Top level nodes
+struct AstBinding       { AstTyped_base; AstNode* node; };
+struct AstAlias         { AstTyped_base; AstTyped* alias; };
+struct AstInclude       { AstNode_base;  AstTyped* name_node; char* name; };
+struct AstInjection     {
+    AstTyped_base;
+    AstTyped* full_loc;
+    AstTyped* to_inject;
+    AstTyped* dest;
+    OnyxToken *symbol;
+};
+struct AstMemRes        {
+    AstTyped_base;
+    AstTyped *initial_value;
+
+    struct Entity *type_entity;
+
+    b32 threadlocal : 1;
+
+    // Set and used in the wasm emission.
+    u32 data_id;
+    u32 tls_offset;
+};
+struct AstGlobal        {
+    AstTyped_base;
+
+    char* name;
+
+    OnyxToken* foreign_module;
+    OnyxToken* foreign_name;
+};
+struct AstParam {
+    // HACK CLEANUP: This does not need to have a local buried inside of it.
+    // Convert this to be AstTyped_base and pull the ParamPassType from AstLocal
+    // to here.                                        - brendanfh 2020/09/18
+    AstLocal *local;
+    AstTyped *default_value;
+
+    VarArgKind vararg_kind;
+    b32 is_used       : 1;
+    b32 use_processed : 1;
+    b32 is_baked      : 1;
+};
+typedef struct OverloadOption OverloadOption;
+struct OverloadOption {
+    // This is u64 because padding will make it that anyway.
+    // Consider: would there be any practical benefit to having the precedence setting
+    // be a compile-time known value? as opposed to a hardcoded value?
+    u64 precedence;
+
+    AstTyped* option;
+};
+
+struct AstOverloadedFunction {
+    AstTyped_base;
+
+    bh_arr(OverloadOption) overloads;
+
+    // CLEANUP: This is unused right now, but should be used to cache
+    // the complete set of overloads that can be used by an overloaded
+    // function.
+    bh_imap            all_overloads;
+
+    b32 locked : 1;
+    b32 only_local_functions : 1;
+};
+
+// @CLEANUP: Is this really necessary?
+typedef struct InterfaceParam {
+    OnyxToken *value_token;
+    OnyxToken *type_token;
+} InterfaceParam;
+
+typedef struct InterfaceConstraint {
+    AstTyped *expr;
+    AstType  *expected_type_expr;
+    Type     *expected_type;
+
+    b32 invert_condition: 1;
+} InterfaceConstraint;
+
+struct AstInterface {
+    AstTyped_base;
+    char *name;
+
+    bh_arr(InterfaceParam)      params;
+    bh_arr(InterfaceConstraint) exprs;
+};
+
+typedef enum ConstraintPhase {
+    Constraint_Phase_Waiting_To_Be_Queued = 0,
+    Constraint_Phase_Cloning_Expressions  = 1,
+    Constraint_Phase_Checking_Expressions = 2,
+    Constraint_Phase_Finished             = 3,
+} ConstraintPhase;
+
+struct AstConstraint {
+    AstNode_base;
+
+    ConstraintPhase phase;
+
+    AstInterface *interface;
+    bh_arr(AstType *) type_args;
+
+    ConstraintCheckStatus *report_status;
+
+    Scope* scope;
+    bh_arr(InterfaceConstraint) exprs;
+    u32 expr_idx;
+};
+
+
+struct AstPackage {
+    AstTyped_base;
+
+    // Allocated in the ast arena
+    char * package_name;
+
+    // NOTE: Symbol nodes
+    bh_arr(OnyxToken *) path;
+
+    Package* package;
+};
+
+//
+// Polymorphic procedures
+//
+
+typedef enum PolyParamKind {
+    // Dont love these names
+    PPK_Undefined,
+    PPK_Poly_Type,
+    PPK_Baked_Value,
+} PolyParamKind;
+
+typedef enum PolySolutionKind {
+    PSK_Undefined,
+    PSK_Type,
+    PSK_Value,
+} PolySolutionKind;
+
+typedef enum PolyProcLookupMethod {
+    PPLM_By_Arguments,
+    PPLM_By_Function_Type,
+} PolyProcLookupMethod;
+
+struct AstPolyParam {
+    PolyParamKind kind;
+
+    // The parameter index where the polymorphic variable occurs.
+    u32 idx;
+
+    // The symbol node that represents the polymorphic variable.
+    AstNode* poly_sym;
+
+    // The type expression that contains `poly_sym` in it somewhere.
+    // Matching polymorphic variables does a parallel tree traversal
+    // to find the pairing of the actual type and polymorphic variable
+    // symbol.
+    AstType* type_expr;
+
+    // Used for baked values. The expected type of the parameter.
+    Type* type;
+};
+
+struct AstPolySolution {
+    PolySolutionKind kind;
+    AstNode* poly_sym;
+
+    union {
+        struct {
+            // If `type` is null, it is filled in with this type.
+            AstType* ast_type;
+            Type*    type;
+        };
+
+        AstTyped* value;
+    };
+};
+
+struct AstSolidifiedFunction {
+    AstFunction   *func;
+    struct Entity *func_header_entity;
+};
+
+struct AstFunction {
+    AstTyped_base;
+
+    Scope *scope;
+
+    bh_arr(AstParam) params;
+    AstType* return_type;
+
+    AstBlock *body;
+
+    char* name;
+
+    // NOTE: This is NULL, unless this function was generated from a polymorphic
+    // procedure call. Then it is set to the token of the call node.
+    OnyxToken* generated_from;
+    Scope*     poly_scope;
+
+    // NOTE: This is NULL, unless this function is used in a "#export" directive.
+    // It is undefined which name it will have if there are multiple export directives
+    // for this particular function.
+    OnyxToken* exported_name;
+
+    OnyxToken* closing_brace;
+
+    union {
+        OnyxToken* intrinsic_name;
+
+        // NOTE: Used when the function is declared as foreign
+        struct {
+            OnyxToken* foreign_module;
+            OnyxToken* foreign_name;
+        };
+    };
+
+    struct Entity* entity_header;
+    struct Entity* entity_body;
+
+    ConstraintContext constraints;
+
+    bh_arr(AstTyped *) tags;
+
+    // Polymorphic procedures use the following fields
+    Scope *parent_scope_of_poly_proc;
+    bh_arr(AstPolyParam) poly_params;
+
+    bh_arr(AstPolySolution) known_slns;
+
+    Table(AstSolidifiedFunction) concrete_funcs;
+    bh_imap active_queries;
+
+    bh_arr(AstNode *) nodes_that_need_entities_after_clone;
+
+    b32 is_exported  : 1;
+    b32 is_foreign   : 1;
+    b32 is_intrinsic : 1;
+};
+
+struct AstPolyQuery {
+    AstNode_base;
+
+    AstFunction *proc;
+    PolyProcLookupMethod pp_lookup;
+    ptr given;
+    OnyxToken *error_loc;
+
+    bh_arr(AstPolySolution) slns;
+
+    AstFunction *function_header;
+
+    b32 error_on_fail : 1;     // Whether or not to report errors on failing to match.
+    b32 successful_symres : 1; // If something successful happened in symbol resolution
+};
+
+
+struct AstDirectiveError {
+    AstNode_base;
+
+    OnyxToken* error_msg;
+};
+
+struct AstDirectiveAddOverload {
+    AstNode_base;
+
+    // NOTE: used by the #add_overload directive. Initially set to a symbol,
+    // then resolved to an overloaded function.
+    AstNode *overloaded_function;
+
+    // See note in OverloadOption. This could be refactored into an OverloadOption?
+    u64 precedence;
+    AstTyped *overload;
+};
+
+struct AstDirectiveOperator {
+    AstNode_base;
+
+    BinaryOp operator;
+    AstTyped *overload;
+};
+
+struct AstDirectiveExport {
+    AstNode_base;
+
+    OnyxToken* export_name;
+    AstTyped* export_name_expr;
+    AstTyped* export;
+};
+
+struct AstDirectiveDefined {
+    AstTyped_base;
+    AstTyped *expr;
+
+    b32 is_defined: 1;
+};
+
+struct AstDirectiveRemove {
+    AstNode_base;
+};
+
+struct AstNote {
+    AstNode_base;
+};
+
+struct AstCallSite {
+    AstTyped_base;
+
+    OnyxToken* callsite_token;
+
+    AstStrLit* filename;
+    AstNumLit* line;
+    AstNumLit* column;
+};
+
+// Represents a "pastable" block of code.
+struct AstCodeBlock {
+    AstTyped_base;
+
+    AstNode *code;
+};
+
+struct AstDirectiveInsert {
+    AstTyped_base;
+
+    AstTyped *code_expr;
+};
+
+struct AstDirectiveInit {
+    AstNode_base;
+
+    AstTyped *init_proc;
+    bh_arr(AstDirectiveInit *) dependencies;
+};
+
+struct AstMacro {
+    AstTyped_base;
+
+    AstTyped* body;
+};
+
+struct AstDirectiveLibrary {
+    AstNode_base;
+
+    AstTyped *library_symbol; // This should resolve to a string literal
+    char *library_name;
+};
+
+struct AstForeignBlock {
+    AstTyped_base;
+
+    Scope* scope;
+    OnyxToken *module_name;
+    bh_arr(struct Entity *) captured_entities;
+
+    u32 foreign_block_number;
+};
+
+typedef enum EntityState {
+    Entity_State_Error,
+    
+    Entity_State_Parse_Builtin,
+    Entity_State_Introduce_Symbols,
+    Entity_State_Parse,
+    Entity_State_Resolve_Symbols,
+    Entity_State_Check_Types,
+    Entity_State_Code_Gen,
+    Entity_State_Finalized,
+    Entity_State_Failed,
+
+    Entity_State_Count,
+} EntityState;
+
+extern const char* entity_state_strings[Entity_State_Count];
+
+// NOTE: An Entity represents something will need to be
+// processed later down the pipeline.
+typedef enum EntityType {
+    Entity_Type_Unknown,
+
+    Entity_Type_Error,
+    Entity_Type_Note,
+    Entity_Type_Load_Path,
+    Entity_Type_Load_File,
+    Entity_Type_Binding,
+    Entity_Type_Use_Package,
+    Entity_Type_Static_If,
+    Entity_Type_String_Literal,
+    Entity_Type_File_Contents,
+    Entity_Type_Enum,
+    Entity_Type_Enum_Value,
+    Entity_Type_Type_Alias,
+    Entity_Type_Memory_Reservation_Type,
+    Entity_Type_Use,
+    Entity_Type_Interface,
+    Entity_Type_Constraint_Check,
+    Entity_Type_Polymorphic_Proc,
+    Entity_Type_Polymorph_Query,
+    Entity_Type_Macro,
+    Entity_Type_Foreign_Block,
+    Entity_Type_Foreign_Function_Header,
+    Entity_Type_Temp_Function_Header,    // Same as a Function_Header, except it disappears after it checks completely.
+    Entity_Type_Function_Header,
+    Entity_Type_Global_Header,
+    Entity_Type_Process_Directive,
+    Entity_Type_Struct_Member_Default,
+    Entity_Type_Memory_Reservation,
+    Entity_Type_Expression,
+    Entity_Type_Global,
+    Entity_Type_Overloaded_Function,
+    Entity_Type_Function,
+
+    Entity_Type_Count,
+} EntityType;
+
+extern const char* entity_type_strings[Entity_Type_Count];
+
+typedef struct Entity {
+    u32 id;
+
+    EntityType type;
+    EntityState state;
+
+    // TODO: Document this!
+    u32 macro_attempts;
+    u32 micro_attempts;
+
+    b32 entered_in_queue : 1;
+
+    Package *package;
+    Scope *scope;
+
+    // TODO: This is incomplete. Add proper cycle detection and halting.
+    // struct Entity *waiting_on;
+
+    union {
+        AstDirectiveError     *error;
+        AstInclude            *include;
+        AstBinding            *binding;
+        AstIf                 *static_if;
+        AstFunction           *function;
+        AstOverloadedFunction *overloaded_function;
+        AstGlobal             *global;
+        AstTyped              *expr;
+        AstStrLit             *strlit;
+        AstFileContents       *file_contents;
+        AstType               *type_alias;
+        AstEnumType           *enum_type;
+        AstEnumValue          *enum_value;
+        AstMemRes             *mem_res;
+        AstFunction           *poly_proc;
+        AstPolyQuery          *poly_query;
+        AstForeignBlock       *foreign_block;
+        AstMacro              *macro;
+        AstUse                *use;
+        AstInterface          *interface;
+        AstConstraint         *constraint;
+        AstDirectiveLibrary   *library;
+    };
+} Entity;
+
+typedef struct EntityHeap {
+    bh_arena entity_arena;
+    bh_arr(Entity *) entities;
+    i32 next_id;
+
+    i32 state_count[Entity_State_Count];
+    i32 type_count[Entity_Type_Count];
+
+    i32 all_count[Entity_State_Count][Entity_Type_Count];
+} EntityHeap;
+
+void entity_heap_init(EntityHeap* entities);
+void entity_heap_insert_existing(EntityHeap* entities, Entity* e);
+Entity* entity_heap_insert(EntityHeap* entities, Entity e);
+Entity* entity_heap_top(EntityHeap* entities);
+void entity_heap_change_top(EntityHeap* entities, Entity* new_top);
+void entity_heap_remove_top(EntityHeap* entities);
+void entity_change_type(EntityHeap* entities, Entity *ent, EntityType new_type);
+
+// If target_arr is null, the entities will be placed directly in the heap.
+void add_entities_for_node(bh_arr(Entity *)* target_arr, AstNode* node, Scope* scope, Package* package);
+
+void symres_entity(Entity* ent);
+void check_entity(Entity* ent);
+void emit_entity(Entity* ent);
+
+struct Package {
+    char *name;
+
+    Scope *scope;
+    Scope *private_scope;
+
+    // NOTE: This tracks all of the 'use package' statements of this package throughout
+    // the code base. This is used when a static if clears and new symbols are introduced.
+    // 'use package' statements have to be reevaluated to pull in the new symbols.
+    bh_arr(Entity *) use_package_entities;
+};
+
+typedef enum CompileAction CompileAction;
+enum CompileAction {
+    ONYX_COMPILE_ACTION_COMPILE,
+    ONYX_COMPILE_ACTION_RUN,
+    ONYX_COMPILE_ACTION_DOCUMENT,
+    ONYX_COMPILE_ACTION_PRINT_HELP,
+};
+
+
+// ROBUSTNESS: The Runtime definitions here must match those in build_opts.onyx!!
+typedef enum Runtime Runtime;
+enum Runtime {
+    Runtime_Unknown = 0,
+    Runtime_Onyx    = 1,
+    Runtime_Wasi    = 2,
+    Runtime_Js      = 3,
+    Runtime_Custom  = 4,
+};
+
+
+typedef struct CompileOptions CompileOptions;
+struct CompileOptions {
+    bh_allocator allocator;
+    CompileAction action;
+
+    u32 verbose_output          : 2;
+    b32 fun_output              : 1;
+    b32 print_function_mappings : 1;
+    b32 print_static_if_results : 1;
+    b32 print_notes             : 1;
+    b32 no_colors               : 1;
+    b32 no_file_contents        : 1;
+    
+    b32 use_post_mvp_features : 1;
+    b32 use_multi_threading   : 1;
+    b32 generate_foreign_info : 1;
+
+    Runtime runtime;
+
+    bh_arr(const char *) included_folders;
+    bh_arr(const char *) files;
+    const char* target_file;
+    const char* documentation_file;
+
+    b32 debug_enabled;
+
+    i32    passthrough_argument_count;
+    char** passthrough_argument_data;
+};
+
+typedef struct Context Context;
+struct Context {
+    Table(Package *)      packages;
+    EntityHeap            entities;
+
+    Scope *global_scope;
+
+    CompileOptions* options;
+
+    bh_arena                  ast_arena;
+    bh_allocator token_alloc, ast_alloc;
+
+    bh_arr(bh_file_contents) loaded_files;
+
+    // NOTE: This is defined in onyxwasm.h
+    struct OnyxWasmModule* wasm_module;
+
+    b32 cycle_almost_detected : 1;
+    b32 cycle_detected : 1;
+};
+
+extern Context context;
+
+// NOTE: Basic internal types constructed in the parser
+extern AstBasicType basic_type_void;
+extern AstBasicType basic_type_bool;
+extern AstBasicType basic_type_i8;
+extern AstBasicType basic_type_u8;
+extern AstBasicType basic_type_i16;
+extern AstBasicType basic_type_u16;
+extern AstBasicType basic_type_i32;
+extern AstBasicType basic_type_u32;
+extern AstBasicType basic_type_i64;
+extern AstBasicType basic_type_u64;
+extern AstBasicType basic_type_f32;
+extern AstBasicType basic_type_f64;
+extern AstBasicType basic_type_rawptr;
+extern AstBasicType basic_type_type_expr; // :TypeExprHack
+
+extern AstBasicType basic_type_int_unsized;
+extern AstBasicType basic_type_float_unsized;
+
+extern AstBasicType basic_type_i8x16;
+extern AstBasicType basic_type_i16x8;
+extern AstBasicType basic_type_i32x4;
+extern AstBasicType basic_type_i64x2;
+extern AstBasicType basic_type_f32x4;
+extern AstBasicType basic_type_f64x2;
+extern AstBasicType basic_type_v128;
+
+// HACK
+// :AutoReturnType
+extern Type type_auto_return;
+extern AstBasicType basic_type_auto_return;
+
+extern OnyxToken builtin_package_token;
+extern AstGlobal builtin_heap_start;
+extern AstGlobal builtin_stack_top;
+extern AstGlobal builtin_tls_base;
+extern AstGlobal builtin_tls_size;
+extern AstType  *builtin_string_type;
+extern AstType  *builtin_cstring_type;
+extern AstType  *builtin_range_type;
+extern Type     *builtin_range_type_type;
+extern AstType  *builtin_vararg_type;
+extern Type     *builtin_vararg_type_type;
+extern AstTyped *builtin_context_variable;
+extern AstType  *builtin_allocator_type;
+extern AstType  *builtin_iterator_type;
+extern AstType  *builtin_callsite_type;
+extern AstType  *builtin_any_type;
+extern AstType  *builtin_code_type;
+extern AstType  *builtin_link_options_type;
+extern AstTyped *type_table_node;
+extern AstTyped *foreign_blocks_node;
+extern AstType  *foreign_block_type;
+extern AstTyped *tagged_procedures_node;
+extern AstFunction *builtin_initialize_data_segments;
+extern AstFunction *builtin_run_init_procedures;
+extern bh_arr(AstFunction *) init_procedures;
+
+typedef struct BuiltinSymbol {
+    char*    package;
+    char*    sym;
+    AstNode* node;
+} BuiltinSymbol;
+
+extern const BuiltinSymbol builtin_symbols[];
+
+typedef struct IntrinsicMap {
+    char*         name;
+    OnyxIntrinsic intrinsic;
+} IntrinsicMap;
+
+typedef Table(OnyxIntrinsic) IntrinsicTable;
+extern IntrinsicTable intrinsic_table;
+
+extern bh_arr(OverloadOption) operator_overloads[Binary_Op_Count];
+
+void initialize_builtins(bh_allocator a);
+void initalize_special_globals();
+void introduce_build_options(bh_allocator a);
+
+
+// NOTE: Useful not inlined functions
+AstTyped* ast_reduce(bh_allocator a, AstTyped* node);
+AstNode* ast_clone(bh_allocator a, void* n);
+AstFunction* clone_function_header(bh_allocator a, AstFunction* func);
+void clone_function_body(bh_allocator a, AstFunction* dest, AstFunction* source);
+
+void promote_numlit_to_larger(AstNumLit* num);
+b32 convert_numlit_to_type(AstNumLit* num, Type* type);
+
+typedef enum TypeMatch {
+    TYPE_MATCH_SUCCESS,
+    TYPE_MATCH_FAILED,
+    TYPE_MATCH_YIELD,
+    TYPE_MATCH_SPECIAL, // Only used for nest polymorph function lookups
+} TypeMatch;
+#define unify_node_and_type(node, type) (unify_node_and_type_((node), (type), 1))
+TypeMatch unify_node_and_type_(AstTyped** pnode, Type* type, b32 permanent);
+Type* resolve_expression_type(AstTyped* node);
+
+i64   get_expression_integer_value(AstTyped* node, b32 *out_is_valid);
+char *get_expression_string_value(AstTyped* node, b32 *out_is_valid);
+
+b32 cast_is_legal(Type* from_, Type* to_, char** err_msg);
+char* get_function_name(AstFunction* func);
+
+AstNode* strip_aliases(AstNode* node);
+
+AstNumLit*       make_bool_literal(bh_allocator, b32 b);
+AstNumLit*       make_int_literal(bh_allocator a, i64 value);
+AstNumLit*       make_float_literal(bh_allocator a, f64 value);
+AstRangeLiteral* make_range_literal(bh_allocator a, AstTyped* low, AstTyped* high);
+AstBinaryOp*     make_binary_op(bh_allocator a, BinaryOp operation, AstTyped* left, AstTyped* right);
+AstArgument*     make_argument(bh_allocator a, AstTyped* value);
+AstFieldAccess*  make_field_access(bh_allocator a, AstTyped* node, char* field);
+AstAddressOf*    make_address_of(bh_allocator a, AstTyped* node);
+AstLocal*        make_local(bh_allocator a, OnyxToken* token, AstType* type_node);
+AstNode*         make_symbol(bh_allocator a, OnyxToken* sym);
+AstUnaryOp*      make_cast(bh_allocator a, AstTyped* expr, Type* to);
+AstZeroValue*    make_zero_value(bh_allocator a, OnyxToken *token, Type* type);
+
+void arguments_initialize(Arguments* args);
+b32 fill_in_arguments(Arguments* args, AstNode* provider, char** err_msg, b32 insert_zero_values);
+void arguments_ensure_length(Arguments* args, u32 count);
+void arguments_copy(Arguments* dest, Arguments* src);
+void arguments_clone(Arguments* dest, Arguments* src);
+void arguments_deep_clone(bh_allocator a, Arguments* dest, Arguments* src);
+void arguments_remove_baked(Arguments* args);
+void arguments_clear_baked_flags(Arguments* args);
+TypeMatch check_arguments_against_type(Arguments* args, TypeFunction* func_type, VarArgKind* va_kind,
+                                 OnyxToken* location, char* func_name, struct OnyxError* error);
+i32 get_argument_buffer_size(TypeFunction* type, Arguments* args);
+
+// GROSS: Using void* to avoid having to cast everything.
+const char* node_get_type_name(void* node);
+
+b32 static_if_resolution(AstIf* static_if);
+
+void insert_poly_sln_into_scope(Scope* scope, AstPolySolution *sln);
+TypeMatch find_polymorphic_sln(AstPolySolution *out, AstPolyParam *param, AstFunction *func, PolyProcLookupMethod pp_lookup, ptr actual, char** err_msg);
+AstFunction* polymorphic_proc_lookup(AstFunction* pp, PolyProcLookupMethod pp_lookup, ptr actual, OnyxToken* tkn);
+AstFunction* polymorphic_proc_solidify(AstFunction* pp, bh_arr(AstPolySolution) slns, OnyxToken* tkn);
+AstNode* polymorphic_proc_try_solidify(AstFunction* pp, bh_arr(AstPolySolution) slns, OnyxToken* tkn);
+AstFunction* polymorphic_proc_build_only_header(AstFunction* pp, PolyProcLookupMethod pp_lookup, ptr actual);
+AstFunction* polymorphic_proc_build_only_header_with_slns(AstFunction* pp, bh_arr(AstPolySolution) slns, b32 error_if_failed);
+b32 potentially_convert_function_to_polyproc(AstFunction *func);
+AstPolyCallType* convert_call_to_polycall(AstCall* call);
+
+void add_overload_option(bh_arr(OverloadOption)* poverloads, u64 precedence, AstTyped* overload);
+AstTyped* find_matching_overload_by_arguments(bh_arr(OverloadOption) overloads, Arguments* args);
+AstTyped* find_matching_overload_by_type(bh_arr(OverloadOption) overloads, Type* type);
+void report_unable_to_match_overload(AstCall* call, bh_arr(OverloadOption) overloads);
+
+void expand_macro(AstCall** pcall, AstFunction* template);
+AstFunction* macro_resolve_header(AstMacro* macro, Arguments* args, OnyxToken* callsite, b32 error_if_failed);
+
+Type* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(AstPolySolution) slns, OnyxFilePos pos, b32 error_if_failed);
+
+// NOTE: Useful inlined functions
+static inline b32 is_lval(AstNode* node) {
+    node = strip_aliases(node);
+
+    if    ((node->kind == Ast_Kind_Local)
+        || (node->kind == Ast_Kind_Param)
+        || (node->kind == Ast_Kind_Global)
+        || (node->kind == Ast_Kind_Dereference)
+        || (node->kind == Ast_Kind_Subscript)
+        || (node->kind == Ast_Kind_Field_Access)
+        || (node->kind == Ast_Kind_Memres)
+        || (node->kind == Ast_Kind_Constraint_Sentinel)) // Bit of a hack, but this makes constraints like 'T->foo()' work.
+        return 1;
+
+    if (node->kind == Ast_Kind_Compound) {
+        b32 all_lval = 1;
+        bh_arr_each(AstTyped *, expr, ((AstCompound *) node)->exprs) all_lval = all_lval && is_lval((AstNode *) *expr);
+        return all_lval;
+    }
+
+    return 0;
+}
+
+static inline b32 binop_is_assignment(BinaryOp binop) {
+    return (binop >= Binary_Op_Assign_Start && binop <= Binary_Op_Assign_End);
+}
+
+static inline b32 binop_is_compare(BinaryOp binop) {
+    return (binop >= Binary_Op_Equal && binop <= Binary_Op_Greater_Equal);
+}
+
+static inline b32 node_is_type(AstNode* node) {
+    node = strip_aliases(node);
+
+    return (node->kind > Ast_Kind_Type_Start) && (node->kind < Ast_Kind_Type_End);
+}
+
+static inline b32 node_is_auto_cast(AstNode* node) {
+    return (node->kind == Ast_Kind_Unary_Op) && (((AstUnaryOp *) node)->operation == Unary_Op_Auto_Cast);
+}
+
+static inline b32 node_is_addressable_literal(AstNode* node) {
+    return (node->kind == Ast_Kind_Struct_Literal)
+        || (node->kind == Ast_Kind_Array_Literal);
+}
+
+static inline Type* get_expression_type(AstTyped* expr) {
+    switch (expr->kind) {
+        case Ast_Kind_Block: case Ast_Kind_If: case Ast_Kind_While: return NULL;
+        default: return expr->type;
+    }
+}
+
+static inline CallingConvention type_function_get_cc(Type* type) {
+    if (type == NULL) return CC_Undefined;
+    if (type->kind != Type_Kind_Function) return CC_Undefined;
+    if (type_is_compound(type->Function.return_type)) return CC_Return_Stack;
+    return CC_Return_Wasm;
+}
+
+static inline ParamPassType type_get_param_pass(Type* type) {
+    if (type_is_structlike_strict(type) && !type_structlike_is_simple(type)) return Param_Pass_By_Implicit_Pointer;
+    return Param_Pass_By_Value;
+}
+
+static inline AstFunction* get_function_from_node(AstNode* node) {
+    if (node->kind == Ast_Kind_Function) return (AstFunction *) node;
+    if (node->kind == Ast_Kind_Polymorphic_Proc) return (AstFunction *) node;
+    if (node->kind == Ast_Kind_Macro) return get_function_from_node((AstNode*) ((AstMacro *) node)->body);
+    return NULL;
+}
+
+static inline void convert_polyproc_to_function(AstFunction *func) {
+    if (func->kind != Ast_Kind_Polymorphic_Proc) return;
+
+    func->kind = Ast_Kind_Function;
+    func->parent_scope_of_poly_proc = NULL;
+    func->poly_params = NULL;
+    func->known_slns = NULL;
+    func->concrete_funcs = NULL;
+    func->active_queries.hashes = NULL;
+    func->active_queries.entries = NULL;
+    func->poly_scope = NULL;
+    func->entity = NULL;
+    func->type = NULL;
+    func->tags = NULL;
+}
+
+static inline void convert_function_to_polyproc(AstFunction *func) {
+    if (func->kind != Ast_Kind_Function) return;
+
+    func->kind = Ast_Kind_Polymorphic_Proc;
+    func->parent_scope_of_poly_proc = func->scope->parent;
+    func->scope = NULL;
+    if (func->entity) entity_change_type(&context.entities, func->entity, Entity_Type_Polymorphic_Proc);
+}
+
+#endif // #ifndef ONYXASTNODES_H
diff --git a/compiler/include/doc.h b/compiler/include/doc.h
new file mode 100644 (file)
index 0000000..75d4bfc
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef ONYXDOC_H
+#define ONYXDOC_H
+
+#include "bh.h"
+#include "astnodes.h"
+
+typedef enum DocFormat {
+    Doc_Format_Human,
+    Doc_Format_Tags,
+    Doc_Format_Html,
+} DocFormat;
+
+typedef struct DocEntry {
+       OnyxFilePos pos;
+    char* sym; // Unused by doc generator
+       char* def;
+       char* additional;
+} DocEntry;
+
+typedef struct DocPackage {
+       const char* name;
+
+       bh_arr(DocEntry) public_entries;
+       bh_arr(DocEntry) private_entries;
+} DocPackage;
+
+typedef struct OnyxDocumentation {
+       bh_arena doc_arena;
+
+    DocFormat format;
+
+       bh_arr(DocPackage) package_docs;
+} OnyxDocumentation;
+
+OnyxDocumentation onyx_docs_generate();
+void onyx_docs_emit(OnyxDocumentation* doc, const char* filename);
+
+#endif
diff --git a/compiler/include/errors.h b/compiler/include/errors.h
new file mode 100644 (file)
index 0000000..9df95de
--- /dev/null
@@ -0,0 +1,44 @@
+#ifndef ONYXERRORS_H
+#define ONYXERRORS_H
+
+#include "bh.h"
+#include "lex.h"
+
+#include <stdarg.h>
+
+typedef enum OnyxErrorRank {
+    Error_Undefined = 0,
+    Error_Other = 1,
+    Error_Warning = 2,
+    Error_Waiting_On = 3,
+    Error_Critical = 4,
+} OnyxErrorRank;
+
+typedef struct OnyxError {
+    OnyxFilePos pos;
+    OnyxErrorRank rank;
+    char *text;
+} OnyxError;
+
+typedef struct OnyxErrors {
+    bh_arena     msg_arena;
+    bh_allocator msg_alloc;
+
+    // NOTE: Pointer to a array of all the loaded files.
+    bh_arr(bh_file_contents)* file_contents;
+
+    bh_arr(OnyxError) errors;
+} OnyxErrors;
+
+extern OnyxErrors msgs;
+
+void onyx_errors_init(bh_arr(bh_file_contents)* files);
+void onyx_submit_error(OnyxError error);
+void onyx_report_error(OnyxFilePos pos, OnyxErrorRank rank, char * format, ...);
+void onyx_submit_warning(OnyxError error);
+void onyx_report_warning(OnyxFilePos pos, char* format, ...);
+void onyx_errors_print();
+b32  onyx_has_errors();
+void onyx_clear_errors();
+
+#endif
diff --git a/compiler/include/lex.h b/compiler/include/lex.h
new file mode 100644 (file)
index 0000000..df68447
--- /dev/null
@@ -0,0 +1,122 @@
+#ifndef ONYXLEX_H
+#define ONYXLEX_H
+
+#include "bh.h"
+
+// NOTE: Used for global statistics
+extern u64 lexer_lines_processed;
+extern u64 lexer_tokens_processed;
+
+typedef enum TokenType {
+    Token_Type_Ascii_End            = 256,
+    Token_Type_Unknown              = 256,
+    Token_Type_End_Stream           = 257,
+
+    Token_Type_Comment              = 258,
+
+    Token_Type_Keyword_Package,
+    Token_Type_Keyword_Struct,
+    Token_Type_Keyword_Enum,
+    Token_Type_Keyword_Use,
+    Token_Type_Keyword_If,
+    Token_Type_Keyword_Else,
+    Token_Type_Keyword_Elseif,
+    Token_Type_Keyword_Return,
+    Token_Type_Keyword_Global,
+    Token_Type_Keyword_Cast,
+    Token_Type_Keyword_While,
+    Token_Type_Keyword_For,
+    Token_Type_Keyword_Break,
+    Token_Type_Keyword_Continue,
+    Token_Type_Keyword_Sizeof,
+    Token_Type_Keyword_Alignof,
+    Token_Type_Keyword_Typeof,
+    Token_Type_Keyword_Defer,
+    Token_Type_Keyword_Do,
+    Token_Type_Keyword_Case,
+    Token_Type_Keyword_Switch,
+    Token_Type_Keyword_Fallthrough,
+    Token_Type_Keyword_Macro,
+    Token_Type_Keyword_Interface,
+    Token_Type_Keyword_Where,
+
+    Token_Type_Right_Arrow,
+    Token_Type_Left_Arrow,
+    Token_Type_Empty_Block,
+    Token_Type_Pipe,
+
+    Token_Type_Greater_Equal,
+    Token_Type_Less_Equal,
+    Token_Type_Equal_Equal,
+    Token_Type_Not_Equal,
+    Token_Type_Plus_Equal,
+    Token_Type_Minus_Equal,
+    Token_Type_Star_Equal,
+    Token_Type_Fslash_Equal,
+    Token_Type_Percent_Equal,
+    Token_Type_And_Equal,
+    Token_Type_Or_Equal,
+    Token_Type_Xor_Equal,
+    Token_Type_And_And,
+    Token_Type_Or_Or,
+    Token_Type_Shift_Left,
+    Token_Type_Shift_Right,
+    Token_Type_Shift_Arith_Right,
+    Token_Type_Shl_Equal,
+    Token_Type_Shr_Equal,
+    Token_Type_Sar_Equal,
+
+    Token_Type_Dot_Dot,
+    Token_Type_Tilde_Tilde,
+
+    Token_Type_Symbol,
+    Token_Type_Literal_String,
+    Token_Type_Literal_Integer,
+    Token_Type_Literal_Float,
+    Token_Type_Literal_True,
+    Token_Type_Literal_False,
+
+    Token_Type_Note,
+
+    Token_Type_Count,
+} TokenType;
+
+typedef struct OnyxFilePos {
+    const char* filename;
+    char* line_start;
+    u32 line;
+
+    // NOTE: This assumes that no line is no longer than 2^16 chars
+    u16 column, length;
+} OnyxFilePos;
+
+typedef struct OnyxToken {
+    TokenType type;
+    i32 length;
+    char* text;
+    OnyxFilePos pos;
+} OnyxToken;
+
+typedef struct OnyxTokenizer {
+    char *start, *curr, *end;
+
+    const char* filename;
+
+    char* line_start;
+    u64 line_number;
+
+    bh_arr(OnyxToken) tokens;
+} OnyxTokenizer;
+
+const char* token_name(TokenType tkn_type);
+void token_toggle_end(OnyxToken* tkn);
+OnyxToken* onyx_get_token(OnyxTokenizer* tokenizer);
+OnyxTokenizer onyx_tokenizer_create(bh_allocator allocator, bh_file_contents *fc);
+void onyx_tokenizer_free(OnyxTokenizer* tokenizer);
+void onyx_lex_tokens(OnyxTokenizer* tokenizer);
+
+b32 token_equals(OnyxToken* tkn1, OnyxToken* tkn2);
+b32 token_text_equals(OnyxToken* tkn, char* text);
+b32 token_same_file(OnyxToken *tkn1, OnyxToken *tkn2);
+
+#endif
diff --git a/compiler/include/parser.h b/compiler/include/parser.h
new file mode 100644 (file)
index 0000000..48889a8
--- /dev/null
@@ -0,0 +1,49 @@
+#ifndef ONYXPARSER_H
+#define ONYXPARSER_H
+
+#include "bh.h"
+
+#include "lex.h"
+#include "errors.h"
+#include "astnodes.h"
+
+typedef struct PolymorphicContext {
+    AstType* root_node;
+    bh_arr(AstPolyParam)* poly_params;
+} PolymorphicContext;
+
+typedef struct OnyxParser {
+    bh_allocator allocator;
+
+    Package *package;
+    Scope *file_scope;
+
+    // NOTE: not used since all tokens are lexed before parsing starts
+    OnyxTokenizer *tokenizer;
+    OnyxToken *prev;
+    OnyxToken *curr;
+
+    PolymorphicContext polymorph_context;
+
+    bh_arr(AstFunction *) current_function_stack;
+    Scope *current_scope;
+    bh_arr(bh_arr(Entity *) *) alternate_entity_placement_stack;
+    bh_arr(OnyxToken *) current_symbol_stack;
+
+    bh_arr(AstFlags) scope_flags;
+
+    bh_arr(AstTyped *) stored_tags;
+
+    u16 tag_depth : 16;
+
+    b32 hit_unexpected_token : 1;
+    b32 parse_calls : 1;
+} OnyxParser;
+
+const char* onyx_ast_node_kind_string(AstKind kind);
+void* onyx_ast_node_new(bh_allocator alloc, i32 size, AstKind kind);
+OnyxParser onyx_parser_create(bh_allocator alloc, OnyxTokenizer *tokenizer);
+void onyx_parser_free(OnyxParser* parser);
+void onyx_parse(OnyxParser *parser);
+
+#endif // #ifndef ONYXPARSER_H
diff --git a/compiler/include/types.h b/compiler/include/types.h
new file mode 100644 (file)
index 0000000..5c2b5e6
--- /dev/null
@@ -0,0 +1,247 @@
+#ifndef ONYXTYPES_H
+#define ONYXTYPES_H
+
+#include "bh.h"
+
+#define POINTER_SIZE 4
+
+enum BasicKind {
+    Basic_Kind_Void,
+
+    Basic_Kind_Bool,
+
+    Basic_Kind_Int_Unsized,
+    Basic_Kind_I8,
+    Basic_Kind_U8,
+    Basic_Kind_I16,
+    Basic_Kind_U16,
+    Basic_Kind_I32,
+    Basic_Kind_U32,
+    Basic_Kind_I64,
+    Basic_Kind_U64,
+
+    Basic_Kind_Float_Unsized,
+    Basic_Kind_F32,
+    Basic_Kind_F64,
+
+    Basic_Kind_Rawptr,
+
+    Basic_Kind_I8X16,
+    Basic_Kind_I16X8,
+    Basic_Kind_I32X4,
+    Basic_Kind_I64X2,
+    Basic_Kind_F32X4,
+    Basic_Kind_F64X2,
+    Basic_Kind_V128,
+
+    Basic_Kind_Type_Index,
+
+    Basic_Kind_Count,
+};
+
+enum BasicFlag {
+    Basic_Flag_Boolean          = BH_BIT(0),
+    Basic_Flag_Integer          = BH_BIT(1),
+    Basic_Flag_Unsigned         = BH_BIT(2),
+    Basic_Flag_Float            = BH_BIT(3),
+    Basic_Flag_Pointer          = BH_BIT(4),
+
+    Basic_Flag_SIMD             = BH_BIT(5),
+
+    Basic_Flag_Type_Index       = BH_BIT(6),
+
+    Basic_Flag_Numeric          = Basic_Flag_Integer | Basic_Flag_Float,
+    Basic_Flag_Ordered          = Basic_Flag_Integer | Basic_Flag_Float | Basic_Flag_Pointer,
+    Basic_Flag_Equality         = Basic_Flag_Ordered | Basic_Flag_Type_Index | Basic_Flag_Boolean,
+    Basic_Flag_Constant_Type    = Basic_Flag_Boolean | Basic_Flag_Numeric | Basic_Flag_Pointer,
+    Basic_Flag_Numeric_Ordered  = Basic_Flag_Numeric | Basic_Flag_Ordered,
+};
+
+typedef struct TypeBasic {
+    enum BasicKind kind;
+    u32 flags;
+    u32 size, alignment; // NOTE: In bytes
+    const char* name;
+} TypeBasic;
+
+// NOTE: Forward declaration for some of the types below
+typedef struct Type Type;
+
+typedef struct StructMember {
+    u32 offset, idx;
+    Type *type;
+
+    // NOTE: Since the name is stored here, it may not be necessary to,
+    // store a hash table of struct members. There realistically will not
+    // be many struct members, and iterating through an array would be
+    // easier and less costly.                  - brendanfh 2020/09/17
+    char *name;
+    struct OnyxToken* token;
+
+    struct AstTyped** initial_value;
+    i32 use_through_pointer_index;
+    b32 included_through_use : 1;
+    b32 used : 1;
+
+    bh_arr(struct AstTyped *) meta_tags;
+} StructMember;
+
+typedef struct TypeWithOffset TypeWithOffset;
+struct TypeWithOffset {
+    Type* type;
+    u32   offset;
+};
+
+typedef enum StructProcessingStatus {
+    SPS_Start,
+    SPS_Members_Done,
+    SPS_Uses_Done,
+} StructProcessingStatus;
+
+#define TYPE_KINDS \
+    TYPE_KIND(Basic, TypeBasic)                                   \
+    TYPE_KIND(Pointer, struct { TypeBasic base; Type *elem; })    \
+    TYPE_KIND(Function, struct {                                  \
+        Type *return_type;                                        \
+        u16 param_count;                                          \
+        u16 needed_param_count;                                   \
+        i16 vararg_arg_pos;                                       \
+        Type* params[];                                           \
+    })                                                            \
+    TYPE_KIND(Struct, struct {                                    \
+        char* name;                                               \
+        u32 size;                                                 \
+        u16 alignment, mem_count;                                 \
+        Table(StructMember *) members;                            \
+        bh_arr(StructMember *) memarr;                            \
+        bh_arr(struct AstPolySolution) poly_sln;                  \
+        bh_arr(TypeWithOffset) linear_members;                    \
+        struct AstType *constructed_from;                         \
+        bh_arr(struct AstTyped *) meta_tags;                      \
+        StructProcessingStatus status;                            \
+    })                                                            \
+    TYPE_KIND(PolyStruct, struct {                                \
+        char* name;                                               \
+        bh_arr(struct AstTyped *) meta_tags;                      \
+    })                                                            \
+    TYPE_KIND(Compound, struct {                                  \
+        u32 count;                                                \
+        u32 size;                                                 \
+        bh_arr(TypeWithOffset) linear_members;                    \
+        Type* types[];                                            \
+    })                                                            \
+    TYPE_KIND(Array, struct { Type* elem; u32 size; u32 count; }) \
+    TYPE_KIND(Slice, struct { Type *elem; })                      \
+    TYPE_KIND(DynArray, struct { Type *elem; })                   \
+    TYPE_KIND(VarArgs, struct { Type *elem; })                    \
+    TYPE_KIND(Enum, struct {                                      \
+        char* name;                                               \
+        Type* backing;                                            \
+        b32   is_flags;                                           \
+    })                                                            \
+    TYPE_KIND(Distinct, struct {                                  \
+        char* name;                                               \
+        Type* base_type;                                          \
+    })
+
+
+typedef enum TypeKind {
+    Type_Kind_Invalid,
+
+#define TYPE_KIND(k, ...) Type_Kind_##k,
+    TYPE_KINDS
+#undef TYPE_KIND
+
+    Type_Kind_Count,
+} TypeKind;
+
+#define TYPE_KIND(k, ...) typedef __VA_ARGS__ Type ## k;
+    TYPE_KINDS
+#undef TYPE_KIND
+
+enum TypeFlag {
+    Type_Flag_Default
+};
+
+struct Type {
+    TypeKind kind;
+
+    u32 id;
+    u32 flags;
+
+    // NOTE(Brendan Hansen): The abstract syntax tree node used to create
+    // the type. Primarily used to look up symbols in scopes that are embedded
+    // in the type.
+    struct AstType* ast_type;
+
+    union {
+#define TYPE_KIND(k, ...) Type##k k;
+        TYPE_KINDS
+#undef TYPE_KIND
+    };
+};
+
+extern bh_imap type_map;
+
+extern Type basic_types[];
+
+struct AstType;
+struct AstFunction;
+struct AstCompound;
+struct AstStructLiteral;
+
+void types_init();
+void types_dump_type_info();
+
+b32 types_are_compatible(Type* t1, Type* t2);
+u32 type_size_of(Type* type);
+u32 type_alignment_of(Type* type);
+Type* type_build_from_ast(bh_allocator alloc, struct AstType* type_node);
+Type* type_build_implicit_type_of_struct_literal(bh_allocator alloc, struct AstStructLiteral* lit);
+
+Type* type_build_function_type(bh_allocator alloc, struct AstFunction* func);
+Type* type_build_compound_type(bh_allocator alloc, struct AstCompound* compound);
+
+Type* type_make_pointer(bh_allocator alloc, Type* to);
+Type* type_make_array(bh_allocator alloc, Type* to, u32 count);
+Type* type_make_slice(bh_allocator alloc, Type* of);
+Type* type_make_dynarray(bh_allocator alloc, Type* of);
+Type* type_make_varargs(bh_allocator alloc, Type* of);
+
+void build_linear_types_with_offset(Type* type, bh_arr(TypeWithOffset)* pdest, u32 offset);
+b32  type_struct_member_apply_use(bh_allocator alloc, Type *s_type, StructMember *smem);
+
+const char* type_get_unique_name(Type* type);
+const char* type_get_name(Type* type);
+u32 type_get_alignment_log2(Type* type);
+Type* type_get_contained_type(Type* type);
+
+b32 type_lookup_member(Type* type, char* member, StructMember* smem);
+b32 type_lookup_member_by_idx(Type* type, i32 idx, StructMember* smem);
+
+i32 type_linear_member_count(Type* type);
+b32 type_linear_member_lookup(Type* type, i32 idx, TypeWithOffset* two);
+i32 type_get_idx_of_linear_member_with_offset(Type* type, u32 offset);
+
+b32 type_struct_is_simple(Type* type);
+
+b32 type_is_pointer(Type* type);
+b32 type_is_rawptr(Type* type);
+b32 type_is_array(Type* tyoe);
+b32 type_is_struct(Type* type);
+b32 type_is_bool(Type* type);
+b32 type_is_small_integer(Type* type);
+b32 type_is_integer(Type* type);
+b32 type_is_numeric(Type* type);
+b32 type_is_compound(Type* type);
+b32 type_is_simd(Type* type);
+b32 type_results_in_void(Type* type);
+b32 type_is_array_accessible(Type* type);
+b32 type_is_structlike(Type* type);
+b32 type_is_structlike_strict(Type* type);
+u32 type_structlike_mem_count(Type* type);
+u32 type_structlike_is_simple(Type* type);
+b32 type_is_sl_constructable(Type* type);
+b32 type_struct_constructed_from_poly_struct(Type* struct_type, struct AstType* from);
+
+#endif // #ifndef ONYX_TYPES
diff --git a/compiler/include/utils.h b/compiler/include/utils.h
new file mode 100644 (file)
index 0000000..2b0083c
--- /dev/null
@@ -0,0 +1,44 @@
+#include "bh.h"
+
+#include "astnodes.h"
+
+extern bh_scratch global_scratch;
+extern bh_allocator global_scratch_allocator;
+
+extern bh_managed_heap global_heap;
+extern bh_allocator global_heap_allocator;
+
+const char* onyx_ast_node_kind_string(AstKind kind);
+
+Package* package_lookup(char* package_name);
+Package* package_lookup_or_create(char* package_name, Scope* parent_scope, bh_allocator alloc, OnyxFilePos pos);
+void package_track_use_package(Package* package, Entity* entity);
+void package_reinsert_use_packages(Package* package);
+
+Scope* scope_create(bh_allocator a, Scope* parent, OnyxFilePos created_at);
+void scope_include(Scope* target, Scope* source, OnyxFilePos pos);
+b32 symbol_introduce(Scope* scope, OnyxToken* tkn, AstNode* symbol);
+b32 symbol_raw_introduce(Scope* scope, char* tkn, OnyxFilePos pos, AstNode* symbol);
+void symbol_builtin_introduce(Scope* scope, char* sym, AstNode *node);
+void symbol_subpackage_introduce(Scope* scope, char* sym, AstPackage *node);
+AstNode* symbol_raw_resolve(Scope* start_scope, char* sym);
+AstNode* symbol_resolve(Scope* start_scope, OnyxToken* tkn);
+AstNode* try_symbol_raw_resolve_from_node(AstNode* node, char* symbol);
+AstNode* try_symbol_resolve_from_node(AstNode* node, OnyxToken* token);
+AstNode* try_symbol_raw_resolve_from_type(Type *type, char* symbol);
+Scope *get_scope_from_node(AstNode *node);
+Scope *get_scope_from_node_or_create(AstNode *node);
+
+void build_all_overload_options(bh_arr(OverloadOption) overloads, bh_imap* all_overloads);
+
+u32 char_to_base16_value(char x);
+
+// Returns the length after processing the string.
+i32 string_process_escape_seqs(char* dest, char* src, i32 len);
+
+u32 levenshtein_distance(const char *str1, const char *str2);
+char *find_closest_symbol_in_scope_and_parents(Scope *scope, char *sym);
+char *find_closest_symbol_in_node(AstNode *node, char *sym);
+
+extern AstTyped node_that_signals_a_yield;
+extern AstTyped node_that_signals_failure;
diff --git a/compiler/include/wasm_emit.h b/compiler/include/wasm_emit.h
new file mode 100644 (file)
index 0000000..3a79e54
--- /dev/null
@@ -0,0 +1,864 @@
+#ifndef ONYXWASM_H
+#define ONYXWASM_H
+
+#include "bh.h"
+
+#include "astnodes.h"
+#include "errors.h"
+
+typedef u8 WasmType;
+
+typedef struct WasmFuncType {
+    // NOTE: For now, WASM only allows for 1 return value.
+    // This may be lifted in the future.
+    i32 param_count;
+    WasmType return_type;
+    WasmType param_types[];
+} WasmFuncType;
+
+#define SIMD_INSTR_MASK   0x10000
+#define EXT_INSTR_MASK    0x20000
+#define ATOMIC_INSTR_MASK 0x40000
+
+typedef enum WasmInstructionType {
+    WI_UNREACHABLE                   = 0x00,
+    WI_NOP                           = 0x01,
+
+    // NOTE: Control flow
+    WI_BLOCK_START                   = 0x02,
+    WI_BLOCK_END                     = 0x0B, // NOTE: These ends are not unique
+    WI_LOOP_START                    = 0x03,
+    WI_LOOP_END                      = 0x0B,
+    WI_IF_START                      = 0x04,
+    WI_ELSE                          = 0x05,
+    WI_IF_END                        = 0x0B,
+    WI_JUMP                          = 0x0C,
+    WI_COND_JUMP                     = 0x0D,
+    WI_JUMP_TABLE                    = 0x0E,
+    WI_RETURN                        = 0x0F,
+    WI_CALL                          = 0x10,
+    WI_CALL_INDIRECT                 = 0x11,
+
+    // NOTE: Parametric instructions
+    WI_DROP                          = 0x1A,
+    WI_SELECT                        = 0x1B,
+
+    // NOTE: Variable instructions
+    WI_LOCAL_GET                     = 0x20,
+    WI_LOCAL_SET                     = 0x21,
+    WI_LOCAL_TEE                     = 0x22,
+    WI_GLOBAL_GET                    = 0x23,
+    WI_GLOBAL_SET                    = 0x24,
+
+    // NOTE: Memory instructions
+    WI_I32_LOAD                      = 0x28,
+    WI_I64_LOAD                      = 0x29,
+    WI_F32_LOAD                      = 0x2A,
+    WI_F64_LOAD                      = 0x2B,
+    WI_I32_LOAD_8_S                  = 0x2C,
+    WI_I32_LOAD_8_U                  = 0x2D,
+    WI_I32_LOAD_16_S                 = 0x2E,
+    WI_I32_LOAD_16_U                 = 0x2F,
+    WI_I64_LOAD_8_S                  = 0x30,
+    WI_I64_LOAD_8_U                  = 0x31,
+    WI_I64_LOAD_16_S                 = 0x32,
+    WI_I64_LOAD_16_U                 = 0x33,
+    WI_I64_LOAD_32_S                 = 0x34,
+    WI_I64_LOAD_32_U                 = 0x35,
+    WI_I32_STORE                     = 0x36,
+    WI_I64_STORE                     = 0x37,
+    WI_F32_STORE                     = 0x38,
+    WI_F64_STORE                     = 0x39,
+    WI_I32_STORE_8                   = 0x3A,
+    WI_I32_STORE_16                  = 0x3B,
+    WI_I64_STORE_8                   = 0x3C,
+    WI_I64_STORE_16                  = 0x3D,
+    WI_I64_STORE_32                  = 0x3E,
+    WI_MEMORY_SIZE                   = 0x3F,
+    WI_MEMORY_GROW                   = 0x40,
+
+    // NOTE: Numeric Instructions
+    WI_I32_CONST                     = 0x41,
+    WI_I64_CONST                     = 0x42,
+    WI_F32_CONST                     = 0x43,
+    WI_F64_CONST                     = 0x44,
+
+    WI_I32_EQZ                       = 0x45,
+    WI_I32_EQ                        = 0x46,
+    WI_I32_NE                        = 0x47,
+    WI_I32_LT_S                      = 0x48,
+    WI_I32_LT_U                      = 0x49,
+    WI_I32_GT_S                      = 0x4a,
+    WI_I32_GT_U                      = 0x4b,
+    WI_I32_LE_S                      = 0x4c,
+    WI_I32_LE_U                      = 0x4d,
+    WI_I32_GE_S                      = 0x4e,
+    WI_I32_GE_U                      = 0x4f,
+
+    WI_I64_EQZ                       = 0x50,
+    WI_I64_EQ                        = 0x51,
+    WI_I64_NE                        = 0x52,
+    WI_I64_LT_S                      = 0x53,
+    WI_I64_LT_U                      = 0x54,
+    WI_I64_GT_S                      = 0x55,
+    WI_I64_GT_U                      = 0x56,
+    WI_I64_LE_S                      = 0x57,
+    WI_I64_LE_U                      = 0x58,
+    WI_I64_GE_S                      = 0x59,
+    WI_I64_GE_U                      = 0x5a,
+
+    WI_F32_EQ                        = 0x5b,
+    WI_F32_NE                        = 0x5c,
+    WI_F32_LT                        = 0x5d,
+    WI_F32_GT                        = 0x5e,
+    WI_F32_LE                        = 0x5f,
+    WI_F32_GE                        = 0x60,
+
+    WI_F64_EQ                        = 0x61,
+    WI_F64_NE                        = 0x62,
+    WI_F64_LT                        = 0x63,
+    WI_F64_GT                        = 0x64,
+    WI_F64_LE                        = 0x65,
+    WI_F64_GE                        = 0x66,
+
+    WI_I32_CLZ                       = 0x67,
+    WI_I32_CTZ                       = 0x68,
+    WI_I32_POPCNT                    = 0x69,
+    WI_I32_ADD                       = 0x6a,
+    WI_I32_SUB                       = 0x6b,
+    WI_I32_MUL                       = 0x6c,
+    WI_I32_DIV_S                     = 0x6d,
+    WI_I32_DIV_U                     = 0x6e,
+    WI_I32_REM_S                     = 0x6f,
+    WI_I32_REM_U                     = 0x70,
+    WI_I32_AND                       = 0x71,
+    WI_I32_OR                        = 0x72,
+    WI_I32_XOR                       = 0x73,
+    WI_I32_SHL                       = 0x74,
+    WI_I32_SHR_S                     = 0x75,
+    WI_I32_SHR_U                     = 0x76,
+    WI_I32_ROTL                      = 0x77,
+    WI_I32_ROTR                      = 0x78,
+
+    WI_I64_CLZ                       = 0x79,
+    WI_I64_CTZ                       = 0x7a,
+    WI_I64_POPCNT                    = 0x7b,
+    WI_I64_ADD                       = 0x7c,
+    WI_I64_SUB                       = 0x7d,
+    WI_I64_MUL                       = 0x7e,
+    WI_I64_DIV_S                     = 0x7f,
+    WI_I64_DIV_U                     = 0x80,
+    WI_I64_REM_S                     = 0x81,
+    WI_I64_REM_U                     = 0x82,
+    WI_I64_AND                       = 0x83,
+    WI_I64_OR                        = 0x84,
+    WI_I64_XOR                       = 0x85,
+    WI_I64_SHL                       = 0x86,
+    WI_I64_SHR_S                     = 0x87,
+    WI_I64_SHR_U                     = 0x88,
+    WI_I64_ROTL                      = 0x89,
+    WI_I64_ROTR                      = 0x8a,
+
+    WI_F32_ABS                       = 0x8b,
+    WI_F32_NEG                       = 0x8c,
+    WI_F32_CEIL                      = 0x8d,
+    WI_F32_FLOOR                     = 0x8e,
+    WI_F32_TRUNC                     = 0x8f,
+    WI_F32_NEAREST                   = 0x90,
+    WI_F32_SQRT                      = 0x91,
+    WI_F32_ADD                       = 0x92,
+    WI_F32_SUB                       = 0x93,
+    WI_F32_MUL                       = 0x94,
+    WI_F32_DIV                       = 0x95,
+    WI_F32_MIN                       = 0x96,
+    WI_F32_MAX                       = 0x97,
+    WI_F32_COPYSIGN                  = 0x98,
+
+    WI_F64_ABS                       = 0x99,
+    WI_F64_NEG                       = 0x9a,
+    WI_F64_CEIL                      = 0x9b,
+    WI_F64_FLOOR                     = 0x9c,
+    WI_F64_TRUNC                     = 0x9d,
+    WI_F64_NEAREST                   = 0x9e,
+    WI_F64_SQRT                      = 0x9f,
+    WI_F64_ADD                       = 0xA0,
+    WI_F64_SUB                       = 0xA1,
+    WI_F64_MUL                       = 0xA2,
+    WI_F64_DIV                       = 0xA3,
+    WI_F64_MIN                       = 0xA4,
+    WI_F64_MAX                       = 0xA5,
+    WI_F64_COPYSIGN                  = 0xA6,
+
+    WI_I32_FROM_I64                  = 0xA7,
+    WI_I32_FROM_F32_S                = 0xA8,
+    WI_I32_FROM_F32_U                = 0xA9,
+    WI_I32_FROM_F64_S                = 0xAA,
+    WI_I32_FROM_F64_U                = 0xAB,
+
+    WI_I64_FROM_I32_S                = 0xAC,
+    WI_I64_FROM_I32_U                = 0xAD,
+    WI_I64_FROM_F32_S                = 0xAE,
+    WI_I64_FROM_F32_U                = 0xAF,
+    WI_I64_FROM_F64_S                = 0xB0,
+    WI_I64_FROM_F64_U                = 0xB1,
+
+    WI_F32_FROM_I32_S                = 0xB2,
+    WI_F32_FROM_I32_U                = 0xB3,
+    WI_F32_FROM_I64_S                = 0xB4,
+    WI_F32_FROM_I64_U                = 0xB5,
+    WI_F32_FROM_F64                  = 0xB6,
+
+    WI_F64_FROM_I32_S                = 0xB7,
+    WI_F64_FROM_I32_U                = 0xB8,
+    WI_F64_FROM_I64_S                = 0xB9,
+    WI_F64_FROM_I64_U                = 0xBA,
+    WI_F64_FROM_F32                  = 0xBB,
+
+    WI_I32_REINTERPRET_F32           = 0xBC,
+    WI_I64_REINTERPRET_F64           = 0xBD,
+    WI_F32_REINTERPRET_I32           = 0xBE,
+    WI_F64_REINTERPRET_I64           = 0xBF,
+
+    WI_I32_EXTEND_8_S                = 0xC0,
+    WI_I32_EXTEND_16_S               = 0xC1,
+    WI_I64_EXTEND_8_S                = 0xC2,
+    WI_I64_EXTEND_16_S               = 0xC3,
+    WI_I64_EXTEND_32_S               = 0xC4,
+
+    // Pointer stuff; this will make it easier to switch to 64-bit
+    // pointers, whenever WebAssembly standarizes that.
+    WI_PTR_CONST                     = WI_I32_CONST,
+    WI_PTR_LOAD                      = WI_I32_LOAD,
+    WI_PTR_STORE                     = WI_I32_STORE,
+    WI_PTR_ADD                       = WI_I32_ADD,
+    WI_PTR_SUB                       = WI_I32_SUB,
+    WI_PTR_MUL                       = WI_I32_MUL,
+    WI_PTR_GE                        = WI_I32_GE_U,
+    WI_PTR_GT                        = WI_I32_GT_U,
+    WI_PTR_EQ                        = WI_I32_EQ,
+
+    WI_V128_LOAD                     = SIMD_INSTR_MASK | 0,
+    WI_V128_STORE                    = SIMD_INSTR_MASK | 11,
+
+    WI_V128_CONST                    = SIMD_INSTR_MASK | 12,
+
+    WI_I8X16_SHUFFLE                 = SIMD_INSTR_MASK | 13,
+
+    WI_I8X16_EXTRACT_LANE_S          = SIMD_INSTR_MASK | 21,
+    WI_I8X16_EXTRACT_LANE_U          = SIMD_INSTR_MASK | 22,
+    WI_I8X16_REPLACE_LANE            = SIMD_INSTR_MASK | 23,
+    WI_I16X8_EXTRACT_LANE_S          = SIMD_INSTR_MASK | 24,
+    WI_I16X8_EXTRACT_LANE_U          = SIMD_INSTR_MASK | 25,
+    WI_I16X8_REPLACE_LANE            = SIMD_INSTR_MASK | 26,
+    WI_I32X4_EXTRACT_LANE            = SIMD_INSTR_MASK | 27,
+    WI_I32X4_REPLACE_LANE            = SIMD_INSTR_MASK | 28,
+    WI_I64X2_EXTRACT_LANE            = SIMD_INSTR_MASK | 29,
+    WI_I64X2_REPLACE_LANE            = SIMD_INSTR_MASK | 30,
+    WI_F32X4_EXTRACT_LANE            = SIMD_INSTR_MASK | 31,
+    WI_F32X4_REPLACE_LANE            = SIMD_INSTR_MASK | 32,
+    WI_F64X2_EXTRACT_LANE            = SIMD_INSTR_MASK | 33,
+    WI_F64X2_REPLACE_LANE            = SIMD_INSTR_MASK | 34,
+
+    WI_I8X16_SWIZZLE                 = SIMD_INSTR_MASK | 14,
+    WI_I8X16_SPLAT                   = SIMD_INSTR_MASK | 15,
+    WI_I16X8_SPLAT                   = SIMD_INSTR_MASK | 16,
+    WI_I32X4_SPLAT                   = SIMD_INSTR_MASK | 17,
+    WI_I64X2_SPLAT                   = SIMD_INSTR_MASK | 18,
+    WI_F32X4_SPLAT                   = SIMD_INSTR_MASK | 19,
+    WI_F64X2_SPLAT                   = SIMD_INSTR_MASK | 20,
+
+    WI_I8X16_EQ                      = SIMD_INSTR_MASK | 35,
+    WI_I8X16_NEQ                     = SIMD_INSTR_MASK | 36,
+    WI_I8X16_LT_S                    = SIMD_INSTR_MASK | 37,
+    WI_I8X16_LT_U                    = SIMD_INSTR_MASK | 38,
+    WI_I8X16_GT_S                    = SIMD_INSTR_MASK | 39,
+    WI_I8X16_GT_U                    = SIMD_INSTR_MASK | 40,
+    WI_I8X16_LE_S                    = SIMD_INSTR_MASK | 41,
+    WI_I8X16_LE_U                    = SIMD_INSTR_MASK | 42,
+    WI_I8X16_GE_S                    = SIMD_INSTR_MASK | 43,
+    WI_I8X16_GE_U                    = SIMD_INSTR_MASK | 44,
+
+    WI_I16X8_EQ                      = SIMD_INSTR_MASK | 45,
+    WI_I16X8_NEQ                     = SIMD_INSTR_MASK | 46,
+    WI_I16X8_LT_S                    = SIMD_INSTR_MASK | 47,
+    WI_I16X8_LT_U                    = SIMD_INSTR_MASK | 48,
+    WI_I16X8_GT_S                    = SIMD_INSTR_MASK | 49,
+    WI_I16X8_GT_U                    = SIMD_INSTR_MASK | 50,
+    WI_I16X8_LE_S                    = SIMD_INSTR_MASK | 51,
+    WI_I16X8_LE_U                    = SIMD_INSTR_MASK | 52,
+    WI_I16X8_GE_S                    = SIMD_INSTR_MASK | 53,
+    WI_I16X8_GE_U                    = SIMD_INSTR_MASK | 54,
+
+    WI_I32X4_EQ                      = SIMD_INSTR_MASK | 55,
+    WI_I32X4_NEQ                     = SIMD_INSTR_MASK | 56,
+    WI_I32X4_LT_S                    = SIMD_INSTR_MASK | 57,
+    WI_I32X4_LT_U                    = SIMD_INSTR_MASK | 58,
+    WI_I32X4_GT_S                    = SIMD_INSTR_MASK | 59,
+    WI_I32X4_GT_U                    = SIMD_INSTR_MASK | 60,
+    WI_I32X4_LE_S                    = SIMD_INSTR_MASK | 61,
+    WI_I32X4_LE_U                    = SIMD_INSTR_MASK | 62,
+    WI_I32X4_GE_S                    = SIMD_INSTR_MASK | 63,
+    WI_I32X4_GE_U                    = SIMD_INSTR_MASK | 64,
+
+    WI_F32X4_EQ                      = SIMD_INSTR_MASK | 65,
+    WI_F32X4_NEQ                     = SIMD_INSTR_MASK | 66,
+    WI_F32X4_LT                      = SIMD_INSTR_MASK | 67,
+    WI_F32X4_GT                      = SIMD_INSTR_MASK | 68,
+    WI_F32X4_LE                      = SIMD_INSTR_MASK | 69,
+    WI_F32X4_GE                      = SIMD_INSTR_MASK | 70,
+
+    WI_F64X2_EQ                      = SIMD_INSTR_MASK | 71,
+    WI_F64X2_NEQ                     = SIMD_INSTR_MASK | 72,
+    WI_F64X2_LT                      = SIMD_INSTR_MASK | 73,
+    WI_F64X2_GT                      = SIMD_INSTR_MASK | 74,
+    WI_F64X2_LE                      = SIMD_INSTR_MASK | 75,
+    WI_F64X2_GE                      = SIMD_INSTR_MASK | 76,
+
+    WI_V128_NOT                      = SIMD_INSTR_MASK | 77,
+    WI_V128_AND                      = SIMD_INSTR_MASK | 78,
+    WI_V128_ANDNOT                   = SIMD_INSTR_MASK | 79,
+    WI_V128_OR                       = SIMD_INSTR_MASK | 80,
+    WI_V128_XOR                      = SIMD_INSTR_MASK | 81,
+    WI_V128_BITSELECT                = SIMD_INSTR_MASK | 82,
+
+    WI_I8X16_ABS                     = SIMD_INSTR_MASK | 96,
+    WI_I8X16_NEG                     = SIMD_INSTR_MASK | 97,
+    WI_I8X16_ANY_TRUE                = SIMD_INSTR_MASK | 98,
+    WI_I8X16_ALL_TRUE                = SIMD_INSTR_MASK | 99,
+    WI_I8X16_BITMASK                 = SIMD_INSTR_MASK | 100,
+    WI_I8X16_NARROW_I16X8_S          = SIMD_INSTR_MASK | 101,
+    WI_I8X16_NARROW_I16X8_U          = SIMD_INSTR_MASK | 102,
+    WI_I8X16_SHL                     = SIMD_INSTR_MASK | 107,
+    WI_I8X16_SHR_S                   = SIMD_INSTR_MASK | 108,
+    WI_I8X16_SHR_U                   = SIMD_INSTR_MASK | 109,
+    WI_I8X16_ADD                     = SIMD_INSTR_MASK | 110,
+    WI_I8X16_ADD_SAT_S               = SIMD_INSTR_MASK | 111,
+    WI_I8X16_ADD_SAT_U               = SIMD_INSTR_MASK | 112,
+    WI_I8X16_SUB                     = SIMD_INSTR_MASK | 113,
+    WI_I8X16_SUB_SAT_S               = SIMD_INSTR_MASK | 114,
+    WI_I8X16_SUB_SAT_U               = SIMD_INSTR_MASK | 115,
+    WI_I8X16_MIN_S                   = SIMD_INSTR_MASK | 118,
+    WI_I8X16_MIN_U                   = SIMD_INSTR_MASK | 119,
+    WI_I8X16_MAX_S                   = SIMD_INSTR_MASK | 120,
+    WI_I8X16_MAX_U                   = SIMD_INSTR_MASK | 121,
+    WI_I8X16_AVGR_U                  = SIMD_INSTR_MASK | 123,
+
+    WI_I16X8_ABS                     = SIMD_INSTR_MASK | 128,
+    WI_I16X8_NEG                     = SIMD_INSTR_MASK | 129,
+    WI_I16X8_ANY_TRUE                = SIMD_INSTR_MASK | 130,
+    WI_I16X8_ALL_TRUE                = SIMD_INSTR_MASK | 131,
+    WI_I16X8_BITMASK                 = SIMD_INSTR_MASK | 132,
+    WI_I16X8_NARROW_I32X4_S          = SIMD_INSTR_MASK | 133,
+    WI_I16X8_NARROW_I32X4_U          = SIMD_INSTR_MASK | 134,
+    WI_I16X8_WIDEN_LOW_I8X16_S       = SIMD_INSTR_MASK | 135,
+    WI_I16X8_WIDEN_HIGH_I8X16_S      = SIMD_INSTR_MASK | 136,
+    WI_I16X8_WIDEN_LOW_I8X16_U       = SIMD_INSTR_MASK | 137,
+    WI_I16X8_WIDEN_HIGH_I8X16_U      = SIMD_INSTR_MASK | 138,
+    WI_I16X8_SHL                     = SIMD_INSTR_MASK | 139,
+    WI_I16X8_SHR_S                   = SIMD_INSTR_MASK | 140,
+    WI_I16X8_SHR_U                   = SIMD_INSTR_MASK | 141,
+    WI_I16X8_ADD                     = SIMD_INSTR_MASK | 142,
+    WI_I16X8_ADD_SAT_S               = SIMD_INSTR_MASK | 143,
+    WI_I16X8_ADD_SAT_U               = SIMD_INSTR_MASK | 144,
+    WI_I16X8_SUB                     = SIMD_INSTR_MASK | 145,
+    WI_I16X8_SUB_SAT_S               = SIMD_INSTR_MASK | 146,
+    WI_I16X8_SUB_SAT_U               = SIMD_INSTR_MASK | 147,
+    WI_I16X8_MUL                     = SIMD_INSTR_MASK | 149,
+    WI_I16X8_MIN_S                   = SIMD_INSTR_MASK | 150,
+    WI_I16X8_MIN_U                   = SIMD_INSTR_MASK | 151,
+    WI_I16X8_MAX_S                   = SIMD_INSTR_MASK | 152,
+    WI_I16X8_MAX_U                   = SIMD_INSTR_MASK | 153,
+    WI_I16X8_AVGR_U                  = SIMD_INSTR_MASK | 155,
+
+    WI_I32X4_ABS                     = SIMD_INSTR_MASK | 160,
+    WI_I32X4_NEG                     = SIMD_INSTR_MASK | 161,
+    WI_I32X4_ANY_TRUE                = SIMD_INSTR_MASK | 162,
+    WI_I32X4_ALL_TRUE                = SIMD_INSTR_MASK | 163,
+    WI_I32X4_BITMASK                 = SIMD_INSTR_MASK | 164,
+    WI_I32X4_WIDEN_LOW_I16X8_S       = SIMD_INSTR_MASK | 167,
+    WI_I32X4_WIDEN_HIGH_I16X8_S      = SIMD_INSTR_MASK | 168,
+    WI_I32X4_WIDEN_LOW_I16X8_U       = SIMD_INSTR_MASK | 169,
+    WI_I32X4_WIDEN_HIGH_I16X8_U      = SIMD_INSTR_MASK | 170,
+    WI_I32X4_SHL                     = SIMD_INSTR_MASK | 171,
+    WI_I32X4_SHR_S                   = SIMD_INSTR_MASK | 172,
+    WI_I32X4_SHR_U                   = SIMD_INSTR_MASK | 173,
+    WI_I32X4_ADD                     = SIMD_INSTR_MASK | 174,
+    WI_I32X4_SUB                     = SIMD_INSTR_MASK | 177,
+    WI_I32X4_MUL                     = SIMD_INSTR_MASK | 181,
+    WI_I32X4_MIN_S                   = SIMD_INSTR_MASK | 182,
+    WI_I32X4_MIN_U                   = SIMD_INSTR_MASK | 183,
+    WI_I32X4_MAX_S                   = SIMD_INSTR_MASK | 184,
+    WI_I32X4_MAX_U                   = SIMD_INSTR_MASK | 185,
+
+    WI_I64X2_NEG                     = SIMD_INSTR_MASK | 193,
+    WI_I64X2_SHL                     = SIMD_INSTR_MASK | 203,
+    WI_I64X2_SHR_S                   = SIMD_INSTR_MASK | 204,
+    WI_I64X2_SHR_U                   = SIMD_INSTR_MASK | 205,
+    WI_I64X2_ADD                     = SIMD_INSTR_MASK | 206,
+    WI_I64X2_SUB                     = SIMD_INSTR_MASK | 209,
+    WI_I64X2_MUL                     = SIMD_INSTR_MASK | 213,
+
+    WI_F32X4_ABS                     = SIMD_INSTR_MASK | 224,
+    WI_F32X4_NEG                     = SIMD_INSTR_MASK | 225,
+    WI_F32X4_SQRT                    = SIMD_INSTR_MASK | 227,
+    WI_F32X4_ADD                     = SIMD_INSTR_MASK | 228,
+    WI_F32X4_SUB                     = SIMD_INSTR_MASK | 229,
+    WI_F32X4_MUL                     = SIMD_INSTR_MASK | 230,
+    WI_F32X4_DIV                     = SIMD_INSTR_MASK | 231,
+    WI_F32X4_MIN                     = SIMD_INSTR_MASK | 232,
+    WI_F32X4_MAX                     = SIMD_INSTR_MASK | 233,
+
+    WI_F64X2_ABS                     = SIMD_INSTR_MASK | 236,
+    WI_F64X2_NEG                     = SIMD_INSTR_MASK | 237,
+    WI_F64X2_SQRT                    = SIMD_INSTR_MASK | 239,
+    WI_F64X2_ADD                     = SIMD_INSTR_MASK | 240,
+    WI_F64X2_SUB                     = SIMD_INSTR_MASK | 241,
+    WI_F64X2_MUL                     = SIMD_INSTR_MASK | 242,
+    WI_F64X2_DIV                     = SIMD_INSTR_MASK | 243,
+    WI_F64X2_MIN                     = SIMD_INSTR_MASK | 244,
+    WI_F64X2_MAX                     = SIMD_INSTR_MASK | 245,
+
+    WI_I32X4_TRUNC_SAT_F32X4_S       = SIMD_INSTR_MASK | 248,
+    WI_I32X4_TRUNC_SAT_F32X4_U       = SIMD_INSTR_MASK | 249,
+    WI_F32X4_CONVERT_I32X4_S         = SIMD_INSTR_MASK | 250,
+    WI_F32X4_CONVERT_I32X4_U         = SIMD_INSTR_MASK | 251,
+    
+    
+    WI_MEMORY_INIT                   = EXT_INSTR_MASK | 0x08,
+    WI_MEMORY_COPY                   = EXT_INSTR_MASK | 0x0a,
+    WI_MEMORY_FILL                   = EXT_INSTR_MASK | 0x0b,
+
+    WI_ATOMIC_NOTIFY                 = ATOMIC_INSTR_MASK | 0x00,
+    WI_ATOMIC_WAIT32                 = ATOMIC_INSTR_MASK | 0x01,
+    WI_ATOMIC_WAIT64                 = ATOMIC_INSTR_MASK | 0x02,
+
+    WI_ATOMIC_FENCE                  = ATOMIC_INSTR_MASK | 0x03,
+
+    WI_ATOMIC_I32_LOAD               = ATOMIC_INSTR_MASK | 0x10,
+    WI_ATOMIC_I64_LOAD               = ATOMIC_INSTR_MASK | 0x11,
+    WI_ATOMIC_I32_LOAD8_U            = ATOMIC_INSTR_MASK | 0x12,
+    WI_ATOMIC_I32_LOAD16_U           = ATOMIC_INSTR_MASK | 0x13,
+    WI_ATOMIC_I64_LOAD8_U            = ATOMIC_INSTR_MASK | 0x14,
+    WI_ATOMIC_I64_LOAD16_U           = ATOMIC_INSTR_MASK | 0x15,
+    WI_ATOMIC_I64_LOAD32_U           = ATOMIC_INSTR_MASK | 0x16,
+
+    WI_ATOMIC_I32_STORE              = ATOMIC_INSTR_MASK | 0x17,
+    WI_ATOMIC_I64_STORE              = ATOMIC_INSTR_MASK | 0x18,
+    WI_ATOMIC_I32_STORE8             = ATOMIC_INSTR_MASK | 0x19,
+    WI_ATOMIC_I32_STORE16            = ATOMIC_INSTR_MASK | 0x1a,
+    WI_ATOMIC_I64_STORE8             = ATOMIC_INSTR_MASK | 0x1b,
+    WI_ATOMIC_I64_STORE16            = ATOMIC_INSTR_MASK | 0x1c,
+    WI_ATOMIC_I64_STORE32            = ATOMIC_INSTR_MASK | 0x1d,
+
+    WI_ATOMIC_I32_ADD                = ATOMIC_INSTR_MASK | 0x1e,
+    WI_ATOMIC_I64_ADD                = ATOMIC_INSTR_MASK | 0x1f,
+    WI_ATOMIC_I32_ADD8_U             = ATOMIC_INSTR_MASK | 0x20,
+    WI_ATOMIC_I32_ADD16_U            = ATOMIC_INSTR_MASK | 0x21,
+    WI_ATOMIC_I64_ADD8_U             = ATOMIC_INSTR_MASK | 0x22,
+    WI_ATOMIC_I64_ADD16_U            = ATOMIC_INSTR_MASK | 0x23,
+    WI_ATOMIC_I64_ADD32_U            = ATOMIC_INSTR_MASK | 0x24,
+
+    WI_ATOMIC_I32_SUB                = ATOMIC_INSTR_MASK | 0x25,
+    WI_ATOMIC_I64_SUB                = ATOMIC_INSTR_MASK | 0x26,
+    WI_ATOMIC_I32_SUB8_U             = ATOMIC_INSTR_MASK | 0x27,
+    WI_ATOMIC_I32_SUB16_U            = ATOMIC_INSTR_MASK | 0x28,
+    WI_ATOMIC_I64_SUB8_U             = ATOMIC_INSTR_MASK | 0x29,
+    WI_ATOMIC_I64_SUB16_U            = ATOMIC_INSTR_MASK | 0x2a,
+    WI_ATOMIC_I64_SUB32_U            = ATOMIC_INSTR_MASK | 0x2b,
+
+    WI_ATOMIC_I32_AND                = ATOMIC_INSTR_MASK | 0x2c,
+    WI_ATOMIC_I64_AND                = ATOMIC_INSTR_MASK | 0x2d,
+    WI_ATOMIC_I32_AND8_U             = ATOMIC_INSTR_MASK | 0x2e,
+    WI_ATOMIC_I32_AND16_U            = ATOMIC_INSTR_MASK | 0x2f,
+    WI_ATOMIC_I64_AND8_U             = ATOMIC_INSTR_MASK | 0x30,
+    WI_ATOMIC_I64_AND16_U            = ATOMIC_INSTR_MASK | 0x31,
+    WI_ATOMIC_I64_AND32_U            = ATOMIC_INSTR_MASK | 0x32,
+
+    WI_ATOMIC_I32_OR                 = ATOMIC_INSTR_MASK | 0x33,
+    WI_ATOMIC_I64_OR                 = ATOMIC_INSTR_MASK | 0x34,
+    WI_ATOMIC_I32_OR8_U              = ATOMIC_INSTR_MASK | 0x35,
+    WI_ATOMIC_I32_OR16_U             = ATOMIC_INSTR_MASK | 0x36,
+    WI_ATOMIC_I64_OR8_U              = ATOMIC_INSTR_MASK | 0x37,
+    WI_ATOMIC_I64_OR16_U             = ATOMIC_INSTR_MASK | 0x38,
+    WI_ATOMIC_I64_OR32_U             = ATOMIC_INSTR_MASK | 0x39,
+
+    WI_ATOMIC_I32_XOR                = ATOMIC_INSTR_MASK | 0x3a,
+    WI_ATOMIC_I64_XOR                = ATOMIC_INSTR_MASK | 0x3b,
+    WI_ATOMIC_I32_XOR8_U             = ATOMIC_INSTR_MASK | 0x3c,
+    WI_ATOMIC_I32_XOR16_U            = ATOMIC_INSTR_MASK | 0x3d,
+    WI_ATOMIC_I64_XOR8_U             = ATOMIC_INSTR_MASK | 0x3e,
+    WI_ATOMIC_I64_XOR16_U            = ATOMIC_INSTR_MASK | 0x3f,
+    WI_ATOMIC_I64_XOR32_U            = ATOMIC_INSTR_MASK | 0x40,
+
+    WI_ATOMIC_I32_XCHG               = ATOMIC_INSTR_MASK | 0x41,
+    WI_ATOMIC_I64_XCHG               = ATOMIC_INSTR_MASK | 0x42,
+    WI_ATOMIC_I32_XCHG8_U            = ATOMIC_INSTR_MASK | 0x43,
+    WI_ATOMIC_I32_XCHG16_U           = ATOMIC_INSTR_MASK | 0x44,
+    WI_ATOMIC_I64_XCHG8_U            = ATOMIC_INSTR_MASK | 0x45,
+    WI_ATOMIC_I64_XCHG16_U           = ATOMIC_INSTR_MASK | 0x46,
+    WI_ATOMIC_I64_XCHG32_U           = ATOMIC_INSTR_MASK | 0x47,
+
+    WI_ATOMIC_I32_CMPXCHG            = ATOMIC_INSTR_MASK | 0x48,
+    WI_ATOMIC_I64_CMPXCHG            = ATOMIC_INSTR_MASK | 0x49,
+    WI_ATOMIC_I32_CMPXCHG8_U         = ATOMIC_INSTR_MASK | 0x4a,
+    WI_ATOMIC_I32_CMPXCHG16_U        = ATOMIC_INSTR_MASK | 0x4b,
+    WI_ATOMIC_I64_CMPXCHG8_U         = ATOMIC_INSTR_MASK | 0x4c,
+    WI_ATOMIC_I64_CMPXCHG16_U        = ATOMIC_INSTR_MASK | 0x4d,
+    WI_ATOMIC_I64_CMPXCHG32_U        = ATOMIC_INSTR_MASK | 0x4e,
+} WasmInstructionType;
+
+typedef union {
+    struct {
+        i32 i1, i2;
+    };
+    i64 l;
+    float f;
+    double d;
+    ptr p;
+} WasmInstructionData;
+
+typedef struct WasmInstruction {
+    WasmInstructionType type;
+    WasmInstructionData data;
+} WasmInstruction;
+
+typedef struct BranchTable {
+    u32 count;
+    u32 default_case;
+    u32 cases[];
+} BranchTable;
+
+#define LOCAL_IS_WASM 0x8000000000000
+typedef struct LocalAllocator {
+    u32 param_count;
+
+    u32 allocated[5];
+    u32 freed[5];
+
+    i32 max_stack;
+    i32 curr_stack;
+} LocalAllocator;
+
+typedef struct WasmFunc {
+    i32 type_idx;
+    LocalAllocator locals;
+    bh_arr(WasmInstruction) code;
+    OnyxToken *location;
+} WasmFunc;
+
+typedef struct WasmGlobal {
+    WasmType type;
+    u32 mutable : 1;
+    bh_arr(WasmInstruction) initial_value;
+} WasmGlobal;
+
+typedef enum WasmForeignKind {
+    WASM_FOREIGN_FUNCTION   = 0x00,
+    WASM_FOREIGN_TABLE      = 0x01,
+    WASM_FOREIGN_MEMORY     = 0x02,
+    WASM_FOREIGN_GLOBAL     = 0x03,
+} WasmForeignKind;
+
+typedef struct WasmExport {
+    WasmForeignKind kind;
+    i32 idx;
+} WasmExport;
+
+typedef struct WasmImport {
+    WasmForeignKind kind;
+    union {
+        i32 idx;
+        struct {
+            i32 min, max;
+            b32 shared;
+        };
+    };
+    char *mod, *name;
+} WasmImport;
+
+typedef struct WasmDatum {
+    u32 id;
+    u32 offset_, alignment;
+    u32 length;
+    ptr data;
+} WasmDatum;
+
+typedef enum DatumPatchInfoKind {
+    Datum_Patch_Instruction,
+    Datum_Patch_Data,
+    Datum_Patch_Relative,
+} DatumPatchInfoKind;
+
+//
+// This represents a pointer that should be filled in
+// later when the corresponding data element is placed.
+//
+// There are three kinds of patches:
+//   - Instruction
+//   - Data
+//   - Relative
+//
+// In all cases, the `data_id` member is set to the id
+// of the WasmDatum entry that will be the base address,
+// and then the `offset` member will be added to that.
+//
+// In instruction patches, the `index` member is set
+// to the index of the function where the instruction should
+// be patched. The `location` member is set to the instruction
+// that needs to have its data changed.
+//
+// In data patches, the `index` member is set to the id
+// of the WasmDatum entry that needs to have a part of it
+// updated. The `location` member is the offset into the
+// data to update. It is assumed that 4 bytes will be reserved
+// to be replaced with the pointer value.
+//
+// In relative patches, `index` member is set to the id
+// of the WasmDatum entry that needs to have a part of it
+// updated. The `location` member is the offset into the
+// data to update. The difference between `Data` and `Relative`
+// is that `Relative` *adds* the base address to the current
+// value in the 4 bytes, as opposed to replacing it. As a
+// convenience, if the value is 0 (null), it will remain as
+// 0.
+//
+typedef struct DatumPatchInfo {
+    DatumPatchInfoKind kind;
+    u32 data_id;
+    u32 offset;
+    u32 location;
+    u32 index;
+} DatumPatchInfo;
+
+// Context used when building a constexpr buffer
+typedef struct ConstExprContext {
+   struct OnyxWasmModule *module;
+   ptr data;
+   u32 data_id;
+} ConstExprContext;
+
+typedef enum DeferredStmtType {
+    Deferred_Stmt_Node,
+    Deferred_Stmt_Code,
+} DeferredStmtType;
+
+typedef struct DeferredStmt {
+    DeferredStmtType type;
+    u32 depth;
+    AstDefer *defer_node;
+
+    union {
+        AstNode *stmt;
+        struct {
+            WasmInstruction* instructions;
+            u32 instruction_count;
+        };
+    };
+} DeferredStmt;
+
+typedef struct AllocatedSpace {
+    u64 depth;
+    AstTyped *expr;
+} AllocatedSpace;
+
+typedef struct StrLitInfo {
+    u32 data_id;
+    u32 len;
+} StrLitInfo;
+
+typedef struct PatchInfo {
+    u32 instruction_index;
+} PatchInfo;
+
+typedef struct ForRemoveInfo {
+    // These are WASM locals
+    u64 iterator_remove_func;
+    u64 iterator_data_ptr;
+
+    i32 remove_func_type_idx;
+} ForRemoveInfo;
+
+typedef struct OnyxWasmModule {
+    bh_allocator allocator;
+
+    bh_arena    *extended_instr_data;
+    bh_allocator extended_instr_alloc;
+
+    // NOTE: Mapping ptrs to function / global indicies
+    bh_imap index_map;
+
+    // NOTE: Mapping from local ast node ptrs to indicies or offsets, depending on the mode
+    bh_imap local_map;
+
+    i32 current_func_idx;
+    LocalAllocator* local_alloc;
+
+    // NOTE: Mapping ptrs to elements
+    bh_imap elem_map;
+
+    bh_arr(DeferredStmt)   deferred_stmts;
+    bh_arr(AllocatedSpace) local_allocations; 
+
+    bh_arr(PatchInfo) stack_leave_patches;
+    bh_arr(DatumPatchInfo) data_patches;
+
+    bh_arr(ForRemoveInfo) for_remove_info;
+
+    bh_arr(AstForeignBlock *) foreign_blocks;
+    u32 next_foreign_block_idx;
+
+    bh_arr(AstFunction *) procedures_with_tags;
+
+    // NOTE: Used internally as a map from strings that represent function types,
+    // 0x7f 0x7f : 0x7f ( (i32, i32) -> i32 )
+    // to the function type index if it has been created.
+    Table(i32) type_map;
+
+    Table(StrLitInfo) loaded_file_info;
+    Table(StrLitInfo) string_literals;
+
+    bh_arr(u8)  structured_jump_target;
+    bh_arr(AstLocal*) return_location_stack;   // NOTE: Used for do-block return expressions.
+
+    bh_arr(WasmFuncType*) types; // NOTE: This have to be pointers because the type is variadic in size
+    bh_arr(WasmImport)    imports;
+    Table(WasmExport)     exports;
+    bh_arr(WasmGlobal)    globals;
+    bh_arr(WasmFunc)      funcs;
+    bh_arr(WasmDatum)     data;
+    bh_arr(i32)           elems;
+    bh_arr(char *)        libraries;
+    bh_arr(char *)        library_paths;
+    b32 needs_memory_section;
+    u32 memory_min_size;
+    u32 memory_max_size;
+
+    // NOTE: Set of things used when compiling; not part of the actual module
+    u32 export_count;
+    u32 next_type_idx;
+    u32 next_func_idx;
+    u32 next_global_idx;
+    u32 next_tls_offset;
+    u32 next_elem_idx;
+    u32 foreign_function_count;
+
+    i32 *stack_top_ptr;
+    i32 *tls_size_ptr;
+    i32 *heap_start_ptr;
+    u64 stack_base_idx;
+    CallingConvention curr_cc;
+    i32 null_proc_func_idx;
+
+    b32 has_stack_locals : 1;
+
+#ifdef ENABLE_DEBUG_INFO
+    struct DebugContext *debug_context;
+#endif
+
+} OnyxWasmModule;
+
+typedef struct OnyxWasmLinkOptions {
+    b32 stack_first;
+    u32 stack_size;
+    u32 stack_alignment;
+
+    u32 null_reserve_size;
+
+    b32 import_memory;
+    char *import_memory_module_name;
+    char *import_memory_import_name;
+
+    b32 export_memory;
+    char *export_memory_name;
+
+    b32 export_func_table;
+    char *export_func_table_name;
+
+    u32 memory_min_size;
+    u32 memory_max_size;
+} OnyxWasmLinkOptions;
+
+b32 onyx_wasm_build_link_options_from_node(OnyxWasmLinkOptions *opts, struct AstTyped *node);
+
+OnyxWasmModule onyx_wasm_module_create(bh_allocator alloc);
+void onyx_wasm_module_link(OnyxWasmModule *module, OnyxWasmLinkOptions *options);
+void onyx_wasm_module_free(OnyxWasmModule* module);
+void onyx_wasm_module_write_to_buffer(OnyxWasmModule* module, bh_buffer* buffer);
+void onyx_wasm_module_write_to_file(OnyxWasmModule* module, bh_file file);
+
+#ifdef ENABLE_RUN_WITH_WASMER
+void onyx_run_initialize(b32 debug_enabled);
+b32 onyx_run_wasm(bh_buffer code_buffer, int argc, char *argv[]);
+#endif
+
+#ifdef ENABLE_DEBUG_INFO
+
+typedef enum DebugOpType {
+    DOT_END   = 0b00000000,
+    DOT_SET   = 0b00000001,
+    DOT_PUSHF = 0b00000010,
+    DOT_POPF  = 0b00000011,
+    DOT_SYM   = 0b00000100,
+
+    DOT_INC   = 0b01000000,
+    DOT_DEC   = 0b10000000,
+    DOT_REP   = 0b11000000,
+} DebugOpType;
+
+typedef struct DebugFuncContext {
+    u32 func_index;
+    u32 file_id;
+    u32 line;
+    u32 op_offset;
+    u32 stack_ptr_idx;
+
+    u32 name_length;
+    char *name;
+} DebugFuncContext;
+
+typedef struct DebugFileInfo {
+    u32 file_id;
+    u32 line_count;
+} DebugFileInfo;
+
+typedef enum DebugSymLoc {
+    DSL_REGISTER = 1,
+    DSL_STACK    = 2,
+    DSL_GLOBAL   = 3,
+} DebugSymLoc;
+
+typedef struct DebugSymInfo {
+    u32 sym_id;
+    u32 location_type;
+    u64 location_num;
+    char *name;
+    u32 type;
+} DebugSymInfo;
+
+typedef struct DebugSymPatch {
+    u32 func_idx;
+    u32 sym_id;
+    u64 local_idx;
+} DebugSymPatch;
+
+typedef struct DebugContext {
+    bh_allocator allocator;
+
+    Table(DebugFileInfo) file_info;
+    u32 next_file_id;
+
+    bh_arr(DebugSymInfo)  sym_info;
+    bh_arr(DebugSymPatch) sym_patches;
+    u32 next_sym_id;
+
+    bh_arr(DebugFuncContext) funcs;
+    bh_buffer                op_buffer;
+
+    // Used during building the debug info
+    OnyxToken *last_token;
+    b32 last_op_was_rep : 1;
+} DebugContext;
+
+#endif
+
+#endif
diff --git a/compiler/src/astnodes.c b/compiler/src/astnodes.c
new file mode 100644 (file)
index 0000000..f2c6faf
--- /dev/null
@@ -0,0 +1,1406 @@
+#include "astnodes.h"
+#include "parser.h"
+#include "utils.h"
+
+static const char* ast_node_names[] = {
+    "ERROR",
+    "PACKAGE",
+    "INCLUDE FILE",
+    "INCLUDE FOLDER",
+    "INCLUDE ALL IN FOLDER",
+    "INCLUDE LIBRARY PATH",
+    "MEMORY RESERVATION",
+
+    "BINDING",
+    "ALIAS",
+    "INJECTION",
+    "FUNCTION",
+    "OVERLOADED_FUNCTION",
+    "POLYMORPHIC PROC",
+    "POLYMORPH QUERY",
+    "INTERFACE",
+    "CONSTRAINT",
+    "CONSTRAINT SENTITEL",
+    "BLOCK",
+    "LOCAL",
+    "GLOBAL",
+    "SYMBOL",
+
+    "UN_OP",
+    "BIN_OP",
+
+    "COMPOUND",
+    "NAMED_VALUE",
+
+    "TYPE_START (BAD)",
+    "TYPE",
+    "BASIC_TYPE",
+    "POINTER_TYPE",
+    "FUNCTION_TYPE",
+    "ARRAY TYPE",
+    "SLICE TYPE",
+    "DYNARR TYPE",
+    "VARARG TYPE",
+    "STRUCT TYPE",
+    "POLYMORPHIC STRUCT TYPE",
+    "POLYMORPHIC STRUCT CALL TYPE",
+    "ENUM TYPE",
+    "TYPE_ALIAS",
+    "TYPE RAW ALIAS",
+    "COMPOUND TYPE",
+    "TYPE OF",
+    "DISTINCT TYPE",
+    "TYPE_END (BAD)",
+
+    "STRUCT MEMBER",
+    "ENUM VALUE",
+
+    "NUMERIC LITERAL",
+    "STRING LITERAL",
+    "PARAM",
+    "ARGUMENT",
+    "CALL",
+    "INTRINSIC CALL",
+    "RETURN",
+    "ADDRESS OF",
+    "DEREFERENCE",
+    "ARRAY ACCESS",
+    "SLICE",
+    "FIELD ACCESS",
+    "UNARY FIELD ACCESS",
+    "PIPE",
+    "METHOD_CALL",
+    "RANGE",
+    "SIZE OF",
+    "ALIGN OF",
+    "FILE CONTENTS",
+    "STRUCT LITERAL",
+    "ARRAY LITERAL",
+    "IF EXPRESSION",
+
+    "IF",
+    "FOR",
+    "WHILE",
+    "JUMP",
+    "USE",
+    "DEFER",
+    "SWITCH",
+    "CASE",
+
+    "SOLIDIFY",
+    "STATIC IF",
+    "STATIC ERROR",
+    "ADD OVERLOAD",
+    "OPERATOR OVERLOAD",
+    "EXPORT",
+    "DEFINED",
+    "TAG",
+    "INIT",
+    "LIBRARY",
+    "REMOVE",
+    "CALL SITE",
+
+    "CODE BLOCK",
+    "DIRECTIVE INSERT",
+    "MACRO",
+    "DO BLOCK",
+
+    "FOREIGN BLOCK",
+    "ZERO VALUE",
+
+    "NOTE",
+
+    "AST_NODE_KIND_COUNT",
+};
+
+const char* onyx_ast_node_kind_string(AstKind kind) {
+    return ast_node_names[kind];
+}
+
+const char *binaryop_string[Binary_Op_Count] = {
+    "+", "-", "*", "/", "%",
+    "==", "!=", "<", "<=", ">", ">=",
+    "&", "|", "^", "<<", ">>", ">>>",
+    "&&", "||",
+
+    "NONE",
+    "=", "+=", "-=", "*=", "/=", "%=",
+    "&=", "|=", "^=", "<<=", ">>=", ">>>=",
+    "NONE",
+
+    "|>", "..", "->",
+
+    "[]", "[]=", "^[]",
+};
+
+const char* entity_state_strings[Entity_State_Count] = {
+    "Error",
+    "Parse Builtin",
+    "Introduce Symbols",
+    "Parse",
+    "Resolve Symbols",
+    "Check Types",
+    "Code Gen",
+    "Finalized",
+    "Failed",
+};
+
+const char* entity_type_strings[Entity_Type_Count] = {
+    "Unknown",
+    "Error",
+    "Note",
+    "Add to Load Path",
+    "Load File",
+    "Binding (Declaration)",
+    "Use Package",
+    "Static If",
+    "String Literal",
+    "File Contents",
+    "Enum",
+    "Enum Value",
+    "Type Alias",
+    "Memory Reservation Type",
+    "Use",
+    "Interface",
+    "Constraint Check",
+    "Polymorphic Proc",
+    "Polymorph Query",
+    "Foreign Block",
+    "Macro",
+    "Foreign_Function Header",
+    "Temporary Function Header",
+    "Function Header",
+    "Global Header",
+    "Process Directive",
+    "Struct Member Default",
+    "Memory Reservation",
+    "Expression",
+    "Global",
+    "Overloaded_Function",
+    "Function",
+};
+
+AstNumLit* ast_reduce_type_compare(bh_allocator a, AstBinaryOp* node) {
+    AstType* left =  (AstType *) ast_reduce(a, node->left);
+    AstType* right = (AstType *) ast_reduce(a, node->right);
+
+    Type* left_type  = type_build_from_ast(context.ast_alloc, left);
+    Type* right_type = type_build_from_ast(context.ast_alloc, right);
+
+    AstNumLit* res = onyx_ast_node_new(a, sizeof(AstNumLit), Ast_Kind_NumLit);
+    res->token = node->token;
+    res->flags |= node->flags;
+    res->flags |= Ast_Flag_Comptime;
+    res->type_node = (AstType *) &basic_type_bool;
+    res->type = &basic_types[Basic_Kind_Bool];
+    res->next = node->next;
+
+    switch (node->operation) {
+        case Binary_Op_Equal:     res->value.l = left_type->id == right_type->id; break;
+        case Binary_Op_Not_Equal: res->value.l = left_type->id != right_type->id; break;
+        default: assert(("Bad case in ast_reduce_type_compare", 0));
+    }
+
+    return res;
+}
+
+#define REDUCE_BINOP_ALL(op) \
+    if (type_is_small_integer(res->type) || type_is_bool(res->type)) { \
+        res->value.i = left->value.i op right->value.i; \
+    } else if (type_is_integer(res->type) \
+        || res->type->Basic.kind == Basic_Kind_Int_Unsized \
+        || res->type->kind == Type_Kind_Enum) { \
+        res->value.l = left->value.l op right->value.l; \
+    } else if (res->type->Basic.kind == Basic_Kind_F32) { \
+        res->value.f = left->value.f op right->value.f; \
+    } else if (res->type->Basic.kind == Basic_Kind_F64 || res->type->Basic.kind == Basic_Kind_Float_Unsized) { \
+        res->value.d = left->value.d op right->value.d; \
+    } \
+    break;
+
+#define REDUCE_BINOP_INT(op) \
+    if (type_is_small_integer(res->type) || type_is_bool(res->type)) { \
+        res->value.i = left->value.i op right->value.i; \
+    } else if (type_is_integer(res->type) \
+        || res->type->Basic.kind == Basic_Kind_Int_Unsized \
+        || res->type->kind == Type_Kind_Enum) { \
+        res->value.l = left->value.l op right->value.l; \
+    } \
+    break;
+
+#define REDUCE_BINOP_BOOL(op) \
+    if (type_is_bool(res->type)) { \
+        res->value.i = left->value.i op right->value.i; \
+    } \
+    break;
+
+AstNumLit* ast_reduce_binop(bh_allocator a, AstBinaryOp* node) {
+    AstNumLit* left =  (AstNumLit *) ast_reduce(a, node->left);
+    AstNumLit* right = (AstNumLit *) ast_reduce(a, node->right);
+
+    if (node_is_type((AstNode *) left) && node_is_type((AstNode *) right)) {
+        if (node->operation == Binary_Op_Equal || node->operation == Binary_Op_Not_Equal) {
+            return (AstNumLit *) ast_reduce_type_compare(a, node);
+        }
+    }
+
+    if (left->kind != Ast_Kind_NumLit || right->kind != Ast_Kind_NumLit) {
+        node->left  = (AstTyped *) left;
+        node->right = (AstTyped *) right;
+        return (AstNumLit *) node;
+    }
+
+    AstNumLit* res = onyx_ast_node_new(a, sizeof(AstNumLit), Ast_Kind_NumLit);
+    res->token = node->token;
+    res->flags |= node->flags;
+    res->flags |= Ast_Flag_Comptime;
+    res->type_node = node->type_node;
+    res->type = node->type;
+    res->next = node->next;
+
+    switch (node->operation) {
+    case Binary_Op_Add:           REDUCE_BINOP_ALL(+);
+    case Binary_Op_Minus:         REDUCE_BINOP_ALL(-);
+    case Binary_Op_Multiply:      REDUCE_BINOP_ALL(*);
+    case Binary_Op_Divide:        REDUCE_BINOP_ALL(/);
+    case Binary_Op_Modulus:       REDUCE_BINOP_INT(%);
+
+    case Binary_Op_Equal:         REDUCE_BINOP_ALL(==);
+    case Binary_Op_Not_Equal:     REDUCE_BINOP_ALL(!=);
+    case Binary_Op_Less:          REDUCE_BINOP_ALL(<);
+    case Binary_Op_Less_Equal:    REDUCE_BINOP_ALL(<=);
+    case Binary_Op_Greater:       REDUCE_BINOP_ALL(>);
+    case Binary_Op_Greater_Equal: REDUCE_BINOP_ALL(>=);
+
+    case Binary_Op_And:           REDUCE_BINOP_INT(&);
+    case Binary_Op_Or:            REDUCE_BINOP_INT(|);
+    case Binary_Op_Xor:           REDUCE_BINOP_INT(^);
+    case Binary_Op_Shl:           REDUCE_BINOP_INT(<<);
+    case Binary_Op_Shr:           REDUCE_BINOP_INT(>>);
+    case Binary_Op_Sar:           REDUCE_BINOP_INT(>>);
+
+    case Binary_Op_Bool_And:      REDUCE_BINOP_BOOL(&&);
+    case Binary_Op_Bool_Or:       REDUCE_BINOP_BOOL(||);
+
+    default: break;
+    }
+
+    return res;
+}
+
+#define REDUCE_UNOP(op) \
+    if (type_is_small_integer(unop->type) || type_is_bool(unop->type)) { \
+        res->value.i = op (operand)->value.i; \
+    } else if (type_is_integer(unop->type) || unop->type->Basic.kind == Basic_Kind_Int_Unsized) { \
+        res->value.l = op (operand)->value.l; \
+    } else if (unop->type->Basic.kind == Basic_Kind_F32) { \
+        res->value.f = op (operand)->value.f; \
+    } else if (unop->type->Basic.kind == Basic_Kind_F64 || unop->type->Basic.kind == Basic_Kind_Float_Unsized) { \
+        res->value.d = op (operand)->value.d; \
+    } \
+    break;
+
+#define REDUCE_UNOP_INT(op) \
+    if (type_is_small_integer(unop->type) || type_is_bool(unop->type)) { \
+        res->value.i = op (operand)->value.i; \
+    } else if (type_is_integer(unop->type) || res->type->Basic.kind == Basic_Kind_Int_Unsized) { \
+        res->value.l = op (operand)->value.l; \
+    }
+
+AstTyped* ast_reduce_unaryop(bh_allocator a, AstUnaryOp* unop) {
+    // GROSS
+    AstNumLit* operand = (AstNumLit *) ast_reduce(a, unop->expr);
+    unop->expr = (AstTyped *) operand;
+
+    if (operand->kind != Ast_Kind_NumLit) {
+        return (AstTyped *) unop;
+    }
+
+    AstNumLit* res = onyx_ast_node_new(a, sizeof(AstNumLit), Ast_Kind_NumLit);
+    res->token = unop->token;
+    res->flags |= Ast_Flag_Comptime;
+    res->type_node = unop->type_node;
+    res->type = unop->type;
+    res->next = unop->next;
+
+    switch (unop->operation) {
+        case Unary_Op_Negate: REDUCE_UNOP(-);
+        case Unary_Op_Not: {
+            if (type_is_bool(res->type)) res->value.i = ! (operand)->value.i;
+            break;
+        }
+        case Unary_Op_Bitwise_Not: REDUCE_UNOP_INT(~);
+
+        case Unary_Op_Cast: {
+            #if 0
+            Type* from = operand->type;
+            Type* to   = unop->type;
+
+            if (from->kind == Type_Kind_Enum) from = from->Enum.backing;
+            if (to->kind == Type_Kind_Enum) to = to->Enum.backing;
+
+            if (from->kind != Type_Kind_Pointer && from->kind != Type_Kind_Basic) return (AstTyped *) unop;
+            if (to->kind != Type_Kind_Pointer && to->kind != Type_Kind_Basic) return (AstTyped *) unop;
+
+            b32 from_is_int=0, to_is_int=0;
+            i32 from_size,     to_size;
+
+            from_size   = type_size_of(from);
+            to_size     = type_size_of(to);
+
+            if (from->kind == Type_Kind_Basic) from_is_int = (from->flags & (Basic_Flag_Integer | Basic_Flag_Pointer)) != 0;
+            if (to->kind   == Type_Kind_Basic) to_is_int   = (to->flags   & (Basic_Flag_Integer | Basic_Flag_Pointer)) != 0;
+
+            if (from->kind == Type_Kind_Pointer) from_is_int = 1;
+            if (to->kind   == Type_Kind_Pointer) to_is_int = 1;
+
+            if (from_is_int == to_is_int && from_size == to_size) {
+                // If they are already equal, nothing to do
+                return (AstTyped *) res;
+            }
+
+            if (from_is_int && !to_is_int) {
+                if (from_size == to_size) {
+                    if (from_size == 4) {
+                        res->value.f = (f32) operand->value.i;
+                    }
+                    else if (from_size == 8) {
+                        res->value.d = (f64) operand->value.l;
+                    }
+                } else {
+                    if (from_size == 4) {
+                        res->value.d = (f64) operand->value.i;
+                    }
+                    else if (from_size == 8) {
+                        res->value.f = (f32) operand->value.l;
+                    }
+                }
+            } else {
+                if (from_size == to_size) {
+                    if (from_size == 4) {
+                        res->value.i = (i32) operand->value.f;
+                    }
+                    else if (from_size == 8) {
+                        res->value.l = (i64) operand->value.d;
+                    }
+                } else {
+                    if (from_size == 4) {
+                        res->value.l = (i64) operand->value.f;
+                    }
+                    else if (from_size == 8) {
+                        res->value.i = (i32) operand->value.d;
+                    }
+                }
+            }
+            break;
+
+            #endif
+        }
+
+        default: return (AstTyped *) unop;
+    }
+
+    return (AstTyped *) res;
+}
+
+AstTyped* ast_reduce(bh_allocator a, AstTyped* node) {
+    assert(node->flags & Ast_Flag_Comptime);
+
+    switch (node->kind) {
+        case Ast_Kind_Binary_Op:  return (AstTyped *) ast_reduce_binop(a, (AstBinaryOp *) node);
+        case Ast_Kind_Unary_Op:   return (AstTyped *) ast_reduce_unaryop(a, (AstUnaryOp *) node);
+        case Ast_Kind_Enum_Value: return (AstTyped *) ((AstEnumValue *) node)->value;
+        case Ast_Kind_Alias:      return (AstTyped *) ast_reduce(a, ((AstAlias *) node)->alias);
+        default:                  return node;
+    }
+}
+
+void promote_numlit_to_larger(AstNumLit* num) {
+    assert(num->type != NULL);
+
+    if (type_is_integer(num->type) && num->type->Basic.size <= 4) {
+        // NOTE: Int32, Int16, Int8
+        i64 val = (i64) num->value.i;
+        num->value.l = val;
+        num->type = &basic_types[Basic_Kind_I64];
+    } else if (num->type->Basic.size <= 4) {
+        // NOTE: Float32
+        f64 val = (f64) num->value.f;
+        num->value.d = val;
+        num->type = &basic_types[Basic_Kind_F64];
+    }
+}
+
+// NOTE: Returns 1 if the conversion was successful.
+b32 convert_numlit_to_type(AstNumLit* num, Type* to_type) {
+    if (num->type == NULL)
+        num->type = type_build_from_ast(context.ast_alloc, num->type_node);
+    assert(num->type);
+
+    if (types_are_compatible(num->type, to_type)) return 1;
+    if (!type_is_numeric(to_type)) return 0;
+
+    Type *type = to_type;
+    if (type->kind == Type_Kind_Enum) type = type->Enum.backing;
+    if (type->kind == Type_Kind_Distinct) type = type->Distinct.base_type;
+
+    if (num->type->Basic.kind == Basic_Kind_Int_Unsized) {
+
+        //
+        //  Integer literal auto cast rules:
+        //      - Up in size always works
+        //      - Down in size only works if value is in range of smaller type.
+        //      - Cast to float only works if value is less than the maximum precise value for float size.
+        //
+
+        if (type->Basic.flags & Basic_Flag_Integer) {
+            if (type->Basic.flags & Basic_Flag_Unsigned) {
+                u64 value = (u64) num->value.l;
+                if (type->Basic.size == 8) {
+                    num->type = to_type;
+                    return 1;
+                }
+                switch (type->Basic.size) {
+                    case 1: if (value <= 255) {
+                                num->type = to_type;
+                                return 1;
+                            }
+                    case 2: if (value <= 65535) {
+                                num->type = to_type;
+                                return 1;
+                            }
+                    case 4: if (value <= 4294967295) {
+                                num->type = to_type;
+                                return 1;
+                            }
+                }
+
+                onyx_report_error(num->token->pos, Error_Critical, "Unsigned integer constant with value '%l' does not fit into %d-bits.",
+                        num->value.l,
+                        type->Basic.size * 8);
+
+            } else {
+                i64 value = (i64) num->value.l;
+                switch (type->Basic.size) {
+                    case 1: if (-128ll <= value && value <= 127ll) {
+                                num->value.i = (i32) value;
+                                num->type = to_type;
+                                return 1;
+                            } break;
+                    case 2: if (-32768ll <= value && value <= 32767ll) {
+                                num->value.i = (i32) value;
+                                num->type = to_type;
+                                return 1;
+                            } break;
+                    case 4: if (-2147483648ll <= value && value <= 2147483647ll) {
+                                num->value.i = (i32) value;
+                                num->type = to_type;
+                                return 1;
+                            } break;
+                    case 8: {   num->type = to_type;
+                                return 1;
+                            } break;
+                }
+
+                onyx_report_error(num->token->pos, Error_Critical, "Integer constant with value '%l' does not fit into %d-bits.",
+                        num->value.l,
+                        type->Basic.size * 8);
+            }
+        }
+
+        else if (type->Basic.flags & Basic_Flag_Float) {
+            if (type->Basic.size == 4) {
+                // TODO(Brendan): Check these boundary conditions
+                if (bh_abs(num->value.l) >= (1 << 23)) {
+                    onyx_report_error(num->token->pos, Error_Critical, "Integer '%l' does not fit in 32-bit float exactly.", num->value.l);
+                    return 0;
+                }
+
+                num->type = to_type;
+                num->value.f = (f32) num->value.l;
+                return 1;
+            }
+            if (type->Basic.size == 8) {
+                // TODO(Brendan): Check these boundary conditions
+                if (bh_abs(num->value.l) >= (1ull << 52)) {
+                    onyx_report_error(num->token->pos, Error_Critical, "Integer '%l' does not fit in 64-bit float exactly.", num->value.l);
+                    return 0;
+                }
+
+                num->type = to_type;
+                num->value.d = (f64) num->value.l;
+                return 1;
+            }
+        }
+    }
+    else if (num->type->Basic.kind == Basic_Kind_Float_Unsized) {
+        // NOTE: Floats don't cast to integers implicitly.
+        if ((type->Basic.flags & Basic_Flag_Float) == 0) return 0;
+
+        if (type->Basic.kind == Basic_Kind_F32) {
+            num->value.f = (f32) num->value.d;
+        }
+
+        num->type = to_type;
+        return 1;
+    }
+    else if (num->type->Basic.kind == Basic_Kind_F32) {
+        // NOTE: Floats don't cast to integers implicitly.
+        if ((type->Basic.flags & Basic_Flag_Float) == 0) return 0;
+
+        if (type->Basic.kind == Basic_Kind_F64) {
+            num->value.d = (f64) num->value.f;
+            num->type = to_type;
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+// TODO: This function should be able return a "yield" condition. There
+// are a couple cases that need to yield in order to be correct, like
+// polymorphic functions with a typeof for the return type.
+TypeMatch unify_node_and_type_(AstTyped** pnode, Type* type, b32 permanent) {
+    AstTyped* node = *pnode;
+    if (type == NULL) return TYPE_MATCH_FAILED;
+    if (node == NULL) return TYPE_MATCH_FAILED;
+
+    if (node->kind == Ast_Kind_Struct_Literal && (node->type_node == NULL && node->type == NULL)) {
+        if (node->entity != NULL) return TYPE_MATCH_SUCCESS;
+        if (type->kind == Type_Kind_VarArgs) type = type->VarArgs.elem;
+
+        //
+        // If the structure literal has arguments, and the type is not constructable
+        // using a struct literal, then they cannot be unified. However, if no arguments
+        // are given, e.g. .{}, then any type should be matched, as that is the universal
+        // zero-value.
+        if (!type_is_sl_constructable(type)) {
+            AstStructLiteral *sl = (AstStructLiteral *) node;
+            if (bh_arr_length(sl->args.values) != 0 || bh_arr_length(sl->args.named_values) != 0) {
+                return TYPE_MATCH_FAILED;
+            }
+        }
+
+        // If this shouldn't make permanent changes and submit entities,
+        // just assume that it works and don't submit the entities.
+        if (!permanent) return TYPE_MATCH_SUCCESS;
+
+        node->type = type;
+
+        add_entities_for_node(NULL, (AstNode *) node, NULL, NULL);
+        return TYPE_MATCH_SUCCESS;
+    }
+
+    if (node->kind == Ast_Kind_Array_Literal && node->type == NULL) {
+        if (node->entity != NULL) return TYPE_MATCH_SUCCESS;
+
+        // If this shouldn't make permanent changes and submit entities,
+        // just assume that it works and don't submit the entities.
+        if (!permanent) return TYPE_MATCH_SUCCESS;
+
+        Type* array_type=NULL;
+        switch (type->kind) {
+            case Type_Kind_Array: array_type = type; break;
+            case Type_Kind_Slice: {
+                Type* elem_type = type->Slice.elem;
+                AstArrayLiteral* al = (AstArrayLiteral *) node;
+                array_type = type_make_array(context.ast_alloc, elem_type, bh_arr_length(al->values));
+
+                *pnode = (AstTyped *) make_cast(context.ast_alloc, node, type);
+
+                break;
+            }
+
+            default: assert(0);
+        }
+
+        node->type = array_type;
+        node->flags |= Ast_Flag_Array_Literal_Typed;
+
+        add_entities_for_node(NULL, (AstNode *) node, NULL, NULL);
+        return TYPE_MATCH_SUCCESS;
+    }
+
+    if (node->kind == Ast_Kind_Unary_Field_Access) {
+        AstType* ast_type = type->ast_type;
+        AstNode* resolved = try_symbol_resolve_from_node((AstNode *) ast_type, node->token);
+        if (resolved == NULL) {
+            if (context.cycle_detected) {
+                token_toggle_end(node->token);
+                char *closest = find_closest_symbol_in_node((AstNode *) ast_type, node->token->text);
+                token_toggle_end(node->token);
+
+                if (closest) {
+                    onyx_report_error(node->token->pos, Error_Critical, "'%b' does not exist in '%s'. Did you mean '%s'?",
+                        node->token->text, node->token->length,
+                        type_get_name(type),
+                        closest);
+                    return TYPE_MATCH_FAILED;
+                }
+            }
+
+            return TYPE_MATCH_YIELD;
+        }
+
+        if (permanent) *pnode = (AstTyped *) resolved;
+        return TYPE_MATCH_SUCCESS;
+    }
+
+    if (node->kind == Ast_Kind_Overloaded_Function) {
+        AstTyped* func = find_matching_overload_by_type(((AstOverloadedFunction *) node)->overloads, type);
+        if (func == NULL) return TYPE_MATCH_FAILED;
+        if (func == (AstTyped *) &node_that_signals_a_yield) return TYPE_MATCH_YIELD;
+
+        // HACK: It feels like there should be a better place to flag that a procedure was definitely used.
+        if (func->kind == Ast_Kind_Function)
+            func->flags |= Ast_Flag_Function_Used;
+
+        *pnode = func;
+        node = *pnode;
+    }
+
+    if (node->kind == Ast_Kind_Polymorphic_Proc) {
+        AstFunction* func = polymorphic_proc_lookup((AstFunction *) node, PPLM_By_Function_Type, type, node->token);
+        if (func == NULL) return TYPE_MATCH_FAILED;
+        if (func == (AstFunction *) &node_that_signals_a_yield) return TYPE_MATCH_YIELD;
+
+        *pnode = (AstTyped *) func;
+        node = *pnode;
+    }
+
+    // HACK: NullProcHack
+    // The null_proc matches any procedure, and because of that, will cause a runtime error if you
+    // try to call it.
+    if (type->kind == Type_Kind_Function && (node->flags & Ast_Flag_Proc_Is_Null) != 0) return TYPE_MATCH_SUCCESS;
+
+    // The normal case where everything works perfectly.
+    Type* node_type = get_expression_type(node);
+    if (types_are_compatible(node_type, type)) return TYPE_MATCH_SUCCESS;
+
+    Type* any_type = type_build_from_ast(context.ast_alloc, builtin_any_type);
+    if (any_type == NULL) return TYPE_MATCH_YIELD;
+    i64 any_id = any_type->id;
+
+    if (node_type && node_type->id != any_id && type->id == any_id) return TYPE_MATCH_SUCCESS;
+
+    // Here are some of the ways you can unify a node with a type if the type of the
+    // node does not match the given type:
+    //
+    // If the nodes type is a function type and that function has an automatic return
+    // value placeholder, fill in that placeholder with the actual type.
+    // :AutoReturnType
+    if (node_type && node_type->kind == Type_Kind_Function
+        && node_type->Function.return_type == &type_auto_return
+        && type->kind == Type_Kind_Function) {
+
+        node_type->Function.return_type = type->Function.return_type;
+        return TYPE_MATCH_SUCCESS;
+    }
+
+    // If the node is an auto cast (~~) node, then check to see if the cast is legal
+    // to the destination type, and if it is change the type to cast to.
+    if (node_is_auto_cast((AstNode *) node)) {
+        char* dummy;
+        Type* from_type = get_expression_type(((AstUnaryOp *) node)->expr);
+        if (!from_type || !cast_is_legal(from_type, type, &dummy)) {
+            return TYPE_MATCH_FAILED;
+
+        } else {
+            if (permanent) ((AstUnaryOp *) node)->type = type;
+            return TYPE_MATCH_SUCCESS;
+        }
+    }
+
+    // If the destination type is a slice, then automatically convert arrays, dynamic
+    // arrays, and var args, if they are the same type. This is big convenience feature
+    // that makes working with arrays much easier.
+    // [N] T  -> [] T
+    // [..] T -> [] T
+    // ..T    -> [] T
+    else if (node_type && type->kind == Type_Kind_Slice) {
+        if (node_type->kind == Type_Kind_Array || node_type->kind == Type_Kind_DynArray || node_type->kind == Type_Kind_VarArgs) {
+            char* dummy;
+            b32 legal = cast_is_legal(node_type, type, &dummy);
+            if (permanent && legal) {
+                *pnode = (AstTyped *) make_cast(context.ast_alloc, node, type);
+            }
+
+            return legal ? TYPE_MATCH_SUCCESS : TYPE_MATCH_FAILED;
+        }
+    }
+
+    // If the node is a numeric literal, try to convert it to the destination type.
+    else if (node->kind == Ast_Kind_NumLit) {
+        if (convert_numlit_to_type((AstNumLit *) node, type)) return TYPE_MATCH_SUCCESS;
+    }
+
+    // If the node is a compound expression, and it doesn't have a type created,
+    // recursive call this function with the individual components of the compound
+    // expression.
+    else if (node->kind == Ast_Kind_Compound) {
+        if (type->kind != Type_Kind_Compound) return TYPE_MATCH_FAILED;
+
+        AstCompound* compound = (AstCompound *) node;
+
+        u32 expr_count = bh_arr_length(compound->exprs);
+        if (expr_count != type->Compound.count) return TYPE_MATCH_FAILED;
+
+        fori (i, 0, (i64) expr_count) {
+            TypeMatch tm = unify_node_and_type_(&compound->exprs[i], type->Compound.types[i], permanent);
+            if (tm != TYPE_MATCH_SUCCESS) {
+                return tm;
+            }
+        }
+
+        compound->type = type_build_compound_type(context.ast_alloc, compound);
+
+        return TYPE_MATCH_SUCCESS;
+    }
+
+    else if (node->kind == Ast_Kind_If_Expression) {
+        AstIfExpression* if_expr = (AstIfExpression *) node;
+
+        TypeMatch true_success  = unify_node_and_type_(&if_expr->true_expr,  type, permanent);
+        TypeMatch false_success = unify_node_and_type_(&if_expr->false_expr, type, permanent);
+
+        if (true_success == TYPE_MATCH_SUCCESS && false_success == TYPE_MATCH_SUCCESS) {
+            if (permanent) if_expr->type = type;
+            return TYPE_MATCH_SUCCESS;
+
+        } else if (true_success == TYPE_MATCH_FAILED || false_success == TYPE_MATCH_FAILED) {
+            return TYPE_MATCH_FAILED;
+
+        } else {
+            return TYPE_MATCH_YIELD;
+        }
+    }
+
+    else if (node->kind == Ast_Kind_Alias) {
+        AstAlias* alias = (AstAlias *) node;
+        return unify_node_and_type_(&alias->alias, type, permanent);
+    }
+
+    else if (node->kind == Ast_Kind_Address_Of) {
+        AstAddressOf *address_of = (AstAddressOf *) node;
+        if (address_of->can_be_removed) {
+            if (!permanent) {
+                return unify_node_and_type_(&address_of->expr, type, permanent);
+
+            } else {
+                *pnode = (AstTyped *) address_of->expr;
+                return unify_node_and_type_(pnode, type, permanent);
+            }
+        }
+    }
+
+    else if (node->kind == Ast_Kind_Zero_Value) {
+        if (node_type == NULL) {
+            node->type = type;
+            return TYPE_MATCH_SUCCESS; // Shouldn't this be on the next line? And have node_type == node->type checked?
+        }
+    }
+
+    return TYPE_MATCH_FAILED;
+}
+
+Type* resolve_expression_type(AstTyped* node) {
+    if (node == NULL) return NULL;
+
+    if (node->kind == Ast_Kind_Compound) {
+        bh_arr_each(AstTyped *, expr, ((AstCompound *) node)->exprs) {
+            resolve_expression_type(*expr);
+        }
+
+        node->type = type_build_compound_type(context.ast_alloc, (AstCompound *) node);
+        return node->type;
+    }
+
+    if (node->kind == Ast_Kind_Argument) {
+        node->type = resolve_expression_type(((AstArgument *) node)->value);
+    }
+
+    if (node->kind == Ast_Kind_If_Expression) {
+        AstIfExpression* if_expr = (AstIfExpression *) node;
+
+        Type* ltype = resolve_expression_type(if_expr->true_expr);
+        unify_node_and_type(&if_expr->false_expr, ltype);
+
+        if_expr->type = ltype;
+    }
+
+    if (node->kind == Ast_Kind_Alias) {
+        AstAlias* alias = (AstAlias *) node;
+        alias->type = resolve_expression_type(alias->alias);
+    }
+
+    if (node_is_type((AstNode *) node)) {
+        return &basic_types[Basic_Kind_Type_Index];
+    }
+
+    if (node->kind == Ast_Kind_Array_Literal && node->type == NULL) {
+        AstArrayLiteral* al = (AstArrayLiteral *) node;
+        Type* elem_type = &basic_types[Basic_Kind_Void];
+        if (bh_arr_length(al->values) > 0) {
+            elem_type = resolve_expression_type(al->values[0]);
+        }
+
+        if (elem_type) {
+            node->type = type_make_array(context.ast_alloc, elem_type, bh_arr_length(al->values));
+            node->flags |= Ast_Flag_Array_Literal_Typed;
+
+            if (node->entity == NULL) {
+                add_entities_for_node(NULL, (AstNode *) node, NULL, NULL);
+            }
+        }
+    }
+
+    if (node->kind == Ast_Kind_Struct_Literal && node->type == NULL) {
+        AstStructLiteral* sl = (AstStructLiteral *) node;
+        if (sl->stnode || sl->type_node) return NULL;
+
+        // If values without names are given to a struct literal without
+        // a type, then we cannot implicitly build the type of the struct
+        // literal, as the name of every member cannot be known. Maybe we
+        // could implicitly do something like _1, _2, ... for the members
+        // that we not given names?
+        if (bh_arr_length(sl->args.values) > 0) {
+            return NULL;
+        }
+
+        sl->type = type_build_implicit_type_of_struct_literal(context.ast_alloc, sl);
+        if (sl->type) {
+            add_entities_for_node(NULL, (AstNode *) sl, NULL, NULL);
+        }
+    }
+
+    // If polymorphic procedures HAVE to have a type, most likely
+    // because they are part of a `typeof` expression, they are
+    // assigned a void type. This is cleared before the procedure
+    // is solidified.
+    if (node->kind == Ast_Kind_Polymorphic_Proc) {
+        node->type = &basic_types[Basic_Kind_Void];
+    }
+
+    if (node->kind == Ast_Kind_Macro) {
+        return resolve_expression_type((AstTyped *) ((AstMacro *) node)->body);
+    }
+
+    if (node->type == NULL)
+        node->type = type_build_from_ast(context.ast_alloc, node->type_node);
+
+    if (node->kind == Ast_Kind_NumLit && node->type->kind == Type_Kind_Basic) {
+        if (node->type->Basic.kind == Basic_Kind_Int_Unsized) {
+            b32 big    = bh_abs(((AstNumLit *) node)->value.l) >= (1ull << 32);
+            b32 unsign = ((AstNumLit *) node)->was_hex_literal;
+
+            if      ( big && !unsign) convert_numlit_to_type((AstNumLit *) node, &basic_types[Basic_Kind_I64]);
+            else if ( big &&  unsign) convert_numlit_to_type((AstNumLit *) node, &basic_types[Basic_Kind_U64]);
+            else if (!big && !unsign) convert_numlit_to_type((AstNumLit *) node, &basic_types[Basic_Kind_I32]);
+            else if (!big &&  unsign) convert_numlit_to_type((AstNumLit *) node, &basic_types[Basic_Kind_U32]);
+        }
+        else if (node->type->Basic.kind == Basic_Kind_Float_Unsized) {
+            convert_numlit_to_type((AstNumLit *) node, &basic_types[Basic_Kind_F64]);
+        }
+    }
+
+    return node->type;
+}
+
+i64 get_expression_integer_value(AstTyped* node, b32 *is_valid) {
+    if (!node) return 0;
+
+    resolve_expression_type(node);
+
+    if (is_valid) *is_valid = 1;
+
+    if (node->kind == Ast_Kind_NumLit && type_is_integer(node->type)) {
+        return ((AstNumLit *) node)->value.l;
+    }
+
+    if (node->kind == Ast_Kind_NumLit && type_is_bool(node->type)) {
+        return ((AstNumLit *) node)->value.i;
+    }
+
+    if (node->kind == Ast_Kind_Argument) {
+        return get_expression_integer_value(((AstArgument *) node)->value, is_valid);
+    }
+
+    if (node->kind == Ast_Kind_Size_Of) {
+        return ((AstSizeOf *) node)->size;
+    }
+
+    if (node->kind == Ast_Kind_Align_Of) {
+        return ((AstAlignOf *) node)->alignment;
+    }
+
+    if (node->kind == Ast_Kind_Alias) {
+        return get_expression_integer_value(((AstAlias *) node)->alias, is_valid);
+    }
+
+    if (node->kind == Ast_Kind_Enum_Value) {
+        return get_expression_integer_value(((AstEnumValue *) node)->value, is_valid);
+    }
+
+    if (node_is_type((AstNode*) node)) {
+        Type* type = type_build_from_ast(context.ast_alloc, (AstType *) node);
+        if (type) return type->id;
+    }
+
+    if (is_valid) *is_valid = 0;
+    return 0;
+}
+
+char *get_expression_string_value(AstTyped* node, b32 *out_is_valid) {
+    resolve_expression_type(node);
+
+    if (out_is_valid) *out_is_valid = 1;
+
+    if (node->kind == Ast_Kind_StrLit) {
+        AstStrLit *str = (AstStrLit *) node;
+
+        // CLEANUP: Maybe this should allocate on the heap?
+        // I guess if in all cases the memory is allocated on the heap,
+        // then the caller can free the memory.
+        i8* strdata = bh_alloc_array(global_heap_allocator, i8, str->token->length + 1);
+        i32 length  = string_process_escape_seqs(strdata, str->token->text, str->token->length);
+        strdata[length] = '\0';
+
+        return strdata;
+    }
+
+    if (node->kind == Ast_Kind_Alias) {
+        return get_expression_string_value(((AstAlias *) node)->alias, out_is_valid);
+    }
+
+    if (out_is_valid) *out_is_valid = 0;
+    return NULL;
+}
+
+static const b32 cast_legality[][12] = {
+    /* I8  */ { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
+    /* U8  */ { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
+    /* I16 */ { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
+    /* U16 */ { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
+    /* I32 */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+    /* U32 */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+    /* I64 */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 },
+    /* U64 */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 },
+    /* F32 */ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0 },
+    /* F64 */ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0 },
+    /* PTR */ { 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0 },
+    /* TYP */ { 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1,}
+};
+
+b32 cast_is_legal(Type* from_, Type* to_, char** err_msg) {
+    Type* from = from_;
+    Type* to   = to_;
+
+    if (from == NULL) {
+        if (err_msg) *err_msg = "'from' is null. (Compiler Error)";
+        return 0;
+    }
+    if (to == NULL) {
+        if (err_msg) *err_msg = "'to' is null. (Compiler Error)";
+        return 0;
+    }
+
+    if (from_->id == to_->id) return 1;
+
+    if (from->kind == Type_Kind_Enum) from = from->Enum.backing;
+    if (to->kind == Type_Kind_Enum) to = to->Enum.backing;
+
+    if (from->kind == Type_Kind_Struct || to->kind == Type_Kind_Struct) {
+        *err_msg = "Cannot cast to or from a struct.";
+        return 0;
+    }
+
+    // CLEANUP: These error messages should be a lot better and actually
+    // provide the types of the things in question.
+    if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_Array) {
+        if (!types_are_compatible(to->Slice.elem, from->Array.elem)) {
+            *err_msg = "Array to slice cast is not valid here because the types are different.";
+            return 0;
+        } else {
+            return 1;
+        }
+    }
+
+    if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_DynArray) {
+        //if (!types_are_compatible(to->Slice.elem, from->DynArray.elem)) {
+        if (type_size_of(to->Slice.elem) != type_size_of(from->DynArray.elem)) {
+            *err_msg = "Dynmaic array to slice cast is not valid here because the types are different sizes.";
+            return 0;
+        } else {
+            return 1;
+        }
+    }
+
+    if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_VarArgs) {
+        if (!types_are_compatible(to->Slice.elem, from->VarArgs.elem)) {
+            *err_msg = "Variadic argument to slice cast is not valid here because the types are different.";
+            return 0;
+        } else {
+            return 1;
+        }
+    }
+
+    if (to->kind == Type_Kind_Distinct) {
+        if (!types_are_compatible(to->Distinct.base_type, from)) {
+            // :BadErrorMessage
+            *err_msg = "Cannot convert to a distinct type using the wrong base type.";
+            return 0;
+        } else {
+            return 1;
+        }
+    }
+
+    if (from->kind == Type_Kind_Distinct) {
+        if (!types_are_compatible(from->Distinct.base_type, to)) {
+            // :BadErrorMessage
+            *err_msg = "Cannot convert from a distinct type to the wrong destination type.";
+            return 0;
+        } else {
+            return 1;
+        }
+    }
+
+    if (from->kind == Type_Kind_Slice || to->kind == Type_Kind_Slice) {
+        if ((from->kind != Type_Kind_Slice || to->kind != Type_Kind_Slice)
+            || to->Slice.elem->kind != Type_Kind_Pointer || from->Slice.elem->kind != Type_Kind_Pointer
+            || !types_are_compatible(from->Slice.elem, to->Slice.elem)) {
+            *err_msg = "Cannot only cast between slice types when both are a slice of compatible pointers.";
+            return 0;
+        } else {
+            return 1;
+        }
+    }
+
+    if (from->kind == Type_Kind_DynArray || to->kind == Type_Kind_DynArray) {
+        *err_msg = "Cannot cast to or from a dynamic array.";
+        return 0;
+    }
+
+    if (to->kind == Type_Kind_Function) {
+        *err_msg = "Cannot cast to a function.";
+        return 0;
+    }
+
+    if (   (type_is_simd(to) && !type_is_simd(from))
+        || (!type_is_simd(to) && type_is_simd(from))) {
+        *err_msg = "Can only perform a SIMD cast between SIMD types.";
+        return 0;
+    }
+
+    if (from->kind == Type_Kind_Basic && from->Basic.kind == Basic_Kind_Void) {
+        *err_msg = "Cannot cast from void.";
+        return 0;
+    }
+    i32 fromidx = -1, toidx = -1;
+    if (from->Basic.flags & Basic_Flag_Pointer || from->kind == Type_Kind_Array) {
+        fromidx = 10;
+    }
+    else if (from->Basic.flags & Basic_Flag_Integer) {
+        b32 unsign = (from->Basic.flags & Basic_Flag_Unsigned) != 0;
+
+        fromidx = log2_dumb(from->Basic.size) * 2 + unsign;
+    }
+    else if (from->Basic.flags & Basic_Flag_Float) {
+        if      (from->Basic.size == 4) fromidx = 8;
+        else if (from->Basic.size == 8) fromidx = 9;
+    }
+    else if (from->Basic.flags & Basic_Flag_Boolean) {
+        fromidx = 0;
+    }
+    else if (from->Basic.flags & Basic_Flag_Type_Index) {
+        fromidx = 11;
+    }
+
+    if (to->Basic.flags & Basic_Flag_Pointer || to->kind == Type_Kind_Array) {
+        toidx = 10;
+    }
+    else if (to->Basic.flags & Basic_Flag_Integer) {
+        b32 unsign = (to->Basic.flags & Basic_Flag_Unsigned) != 0;
+
+        toidx = log2_dumb(to->Basic.size) * 2 + unsign;
+    }
+    else if (to->Basic.flags & Basic_Flag_Float) {
+        if      (to->Basic.size == 4) toidx = 8;
+        else if (to->Basic.size == 8) toidx = 9;
+    }
+    else if (to->Basic.flags & Basic_Flag_Boolean) {
+        toidx = 0;
+    }
+    else if (to->Basic.flags & Basic_Flag_Type_Index) {
+        toidx = 11;
+    }
+
+    if (fromidx != -1 && toidx != -1) {
+        if (!cast_legality[fromidx][toidx]) {
+            *err_msg = bh_aprintf(global_heap_allocator, "Cast from '%s' to '%s' is not allowed.", type_get_name(from_), type_get_name(to_));
+            return 0;
+        }
+    } else {
+        *err_msg = bh_aprintf(global_heap_allocator, "Cast from '%s' to '%s' is not allowed.", type_get_name(from_), type_get_name(to_));
+        return 0;
+    }
+
+    *err_msg = NULL;
+    return 1;
+}
+
+char* get_function_name(AstFunction* func) {
+    if (func->kind != Ast_Kind_Function) return "<procedure>";
+
+    if (func->name != NULL) return func->name;
+
+    if (func->exported_name != NULL) {
+        return bh_aprintf(global_scratch_allocator,
+                "EXPORTED:%b",
+                func->exported_name->text,
+                func->exported_name->length);
+    }
+
+    return "<anonymous procedure>";
+}
+
+AstNode* strip_aliases(AstNode* n) {
+    if (n == NULL) return n;
+
+    while (n->kind == Ast_Kind_Alias) n = (AstNode *) ((AstAlias *) n)->alias;
+
+    return n;
+}
+
+AstNumLit* make_bool_literal(bh_allocator a, b32 b) {
+    AstNumLit* bl = onyx_ast_node_new(a, sizeof(AstNumLit), Ast_Kind_NumLit);
+    bl->flags |= Ast_Flag_Comptime;
+    bl->type_node = (AstType *) &basic_type_bool;
+
+    bl->value.i = b ? 1 : 0;
+    return bl;
+}
+
+AstNumLit* make_int_literal(bh_allocator a, i64 i) {
+    AstNumLit* num = onyx_ast_node_new(a, sizeof(AstNumLit), Ast_Kind_NumLit);
+    num->flags |= Ast_Flag_Comptime;
+
+    if (bh_abs(i) >= ((u64) 1 << 32))
+        num->type_node = (AstType *) &basic_type_i64;
+    else
+        num->type_node = (AstType *) &basic_type_i32;
+
+    num->value.l = i;
+    return num;
+}
+
+AstNumLit* make_float_literal(bh_allocator a, f64 d) {
+    // NOTE: Use convert_numlit_to_type to make this a concrete float
+    AstNumLit* num = onyx_ast_node_new(a, sizeof(AstNumLit), Ast_Kind_NumLit);
+    num->flags |= Ast_Flag_Comptime;
+    num->type_node = (AstType *) &basic_type_float_unsized;
+    num->value.d = d;
+    return num;
+}
+
+AstRangeLiteral* make_range_literal(bh_allocator a, AstTyped* low, AstTyped* high) {
+    AstRangeLiteral* rl = onyx_ast_node_new(a, sizeof(AstRangeLiteral), Ast_Kind_Range_Literal);
+    rl->type = builtin_range_type_type;
+    rl->low = low;
+    rl->high = high;
+    return rl;
+}
+
+AstBinaryOp* make_binary_op(bh_allocator a, BinaryOp operation, AstTyped* left, AstTyped* right) {
+    AstBinaryOp* binop_node = onyx_ast_node_new(a, sizeof(AstBinaryOp), Ast_Kind_Binary_Op);
+    binop_node->left  = left;
+    binop_node->right = right;
+    binop_node->operation = operation;
+    return binop_node;
+}
+
+AstArgument* make_argument(bh_allocator a, AstTyped* value) {
+    AstArgument* arg = onyx_ast_node_new(a, sizeof(AstArgument), Ast_Kind_Argument);
+    if (value->token) arg->token = value->token;
+    arg->value = value;
+    arg->type = value->type;
+    arg->next = NULL;
+    arg->va_kind = VA_Kind_Not_VA;
+    return arg;
+}
+
+AstFieldAccess* make_field_access(bh_allocator a, AstTyped* node, char* field) {
+    AstFieldAccess* fa = onyx_ast_node_new(a, sizeof(AstFieldAccess), Ast_Kind_Field_Access);
+    if (node->token) fa->token = node->token;
+    fa->field = field;
+    fa->expr = node;
+
+    return fa;
+}
+
+AstAddressOf* make_address_of(bh_allocator a, AstTyped* node) {
+    AstAddressOf* ao = onyx_ast_node_new(a, sizeof(AstAddressOf), Ast_Kind_Address_Of);
+    if (node->token) ao->token = node->token;
+    ao->expr = node;
+
+    return ao;
+}
+
+AstLocal* make_local(bh_allocator a, OnyxToken* token, AstType* type_node) {
+    AstLocal* local = onyx_ast_node_new(a, sizeof(AstLocal), Ast_Kind_Local);
+    local->token = token;
+    local->type_node = type_node;
+
+    return local;
+}
+
+AstNode* make_symbol(bh_allocator a, OnyxToken* sym) {
+    AstNode* symbol = onyx_ast_node_new(a, sizeof(AstNode), Ast_Kind_Symbol);
+    symbol->token = sym;
+    return symbol;
+}
+
+AstUnaryOp* make_cast(bh_allocator a, AstTyped* expr, Type* to) {
+    AstUnaryOp* cast = onyx_ast_node_new(a, sizeof(AstUnaryOp), Ast_Kind_Unary_Op);
+    cast->token = expr->token;
+    cast->operation = Unary_Op_Cast;
+    cast->expr = expr;
+    cast->type = to;
+    return cast;
+}
+
+AstZeroValue* make_zero_value(bh_allocator a, OnyxToken* token, Type* type) {
+    AstZeroValue* zero_value = onyx_ast_node_new(a, sizeof(AstZeroValue), Ast_Kind_Zero_Value);
+    zero_value->token = token;
+    zero_value->flags |= Ast_Flag_Comptime;
+    zero_value->type = type;
+    return zero_value;
+}
+
+void arguments_initialize(Arguments* args) {
+    if (args->values == NULL)       bh_arr_new(global_heap_allocator, args->values, 2);
+    if (args->named_values == NULL) bh_arr_new(global_heap_allocator, args->named_values, 2);
+
+    // CLEANUP: I'm not sure if I need to initialize these to NULL values, but it doesn't hurt.
+    fori (i, 0, 2) {
+        args->values[i] = NULL;
+        args->named_values[i] = NULL;
+    }
+
+    args->used_argument_count = -1;
+}
+
+void arguments_ensure_length(Arguments* args, u32 count) {
+    // Make the array big enough
+    bh_arr_grow(args->values, count);
+
+    // NULL initialize the new elements
+    fori (i, bh_arr_length(args->values), count) args->values[i] = NULL;
+
+    // Set the actual length to the count, but never let it decrease in size
+    bh_arr_set_length(args->values, bh_max(count, (u32) bh_arr_length(args->values)));
+}
+
+void arguments_copy(Arguments* dest, Arguments* src) {
+    dest->used_argument_count = -1;
+    dest->named_values = src->named_values;
+
+    bh_arr_grow(dest->values, (u32) bh_arr_length(src->values));
+    bh_arr_set_length(dest->values, (u32) bh_arr_length(src->values));
+    bh_arr_each(AstTyped*, arg, dest->values) *arg = NULL;
+    fori (i, 0, bh_arr_length(src->values)) dest->values[i] = src->values[i];
+}
+
+// In clone, the named_values are not copied. This is used in find_matching_overload_by_arguments since it doesn't need them to be copied.
+void arguments_clone(Arguments* dest, Arguments* src) {
+    dest->used_argument_count = -1;
+    dest->named_values = src->named_values;
+    dest->values = bh_arr_copy(global_heap_allocator, src->values);
+}
+
+void arguments_deep_clone(bh_allocator a, Arguments* dest, Arguments* src) {
+    dest->used_argument_count = -1;
+    dest->values = NULL;
+    dest->named_values = NULL;
+
+    bh_arr_new(global_heap_allocator, dest->named_values, bh_arr_length(src->named_values));
+    bh_arr_new(global_heap_allocator, dest->values, bh_arr_length(src->values));
+
+    bh_arr_each(AstNamedValue *, nv, src->named_values)
+        bh_arr_push(dest->named_values, (AstNamedValue *) ast_clone(a, *nv));
+
+    bh_arr_each(AstTyped *, val, src->values)
+        bh_arr_push(dest->values, (AstTyped *) ast_clone(a, (AstNode *) *val));
+}
+
+void arguments_remove_baked(Arguments* args) {
+    fori (i, 0, bh_arr_length(args->values)) {
+        if (args->values[i]->kind != Ast_Kind_Argument) continue;
+        if (!((AstArgument *) args->values[i])->is_baked) continue;
+
+        bh_arr_deleten(args->values, i, 1);
+        i--;
+    }
+
+    fori (i, 0, bh_arr_length(args->named_values)) {
+        if (args->named_values[i]->value->kind != Ast_Kind_Argument) continue;
+        if (!((AstArgument *) args->named_values[i]->value)->is_baked) continue;
+
+        bh_arr_deleten(args->named_values, i, 1);
+        i--;
+    }
+}
+
+void arguments_clear_baked_flags(Arguments* args) {
+    bh_arr_each(AstTyped *, arg, args->values) {
+        if ((*arg)->kind == Ast_Kind_Argument) {
+            ((AstArgument *) *arg)->is_baked = 0;
+        }
+    }
+
+    bh_arr_each(AstNamedValue *, arg, args->named_values) {
+        if ((*arg)->value->kind == Ast_Kind_Argument) {
+            ((AstArgument *) (*arg)->value)->is_baked = 0;
+        }
+    }
+}
+
+// GROSS: Using void* to avoid having to cast everything.
+const char* node_get_type_name(void* node) {
+    if (node_is_type((AstNode *) node)) return "type_expr";
+
+    if (((AstNode *) node)->kind == Ast_Kind_Argument) {
+        return node_get_type_name(((AstArgument *) node)->value);
+    }
+
+    if (((AstNode *) node)->kind == Ast_Kind_Polymorphic_Proc) {
+        return "polymorphic procedure";
+    }
+
+    return type_get_name(((AstTyped *) node)->type);
+}
+
+b32 static_if_resolution(AstIf* static_if) {
+    if (static_if->kind != Ast_Kind_Static_If) return 0;
+
+    // assert(condition_value->kind == Ast_Kind_NumLit); // This should be right, right?
+    i64 value = get_expression_integer_value(static_if->cond, NULL);
+
+    return value != 0;
+}
+
+AstPolyCallType* convert_call_to_polycall(AstCall* call) {
+    // HACK HACK HACK
+    AstPolyCallType *pct = onyx_ast_node_new(context.ast_alloc, sizeof(AstPolyCallType), Ast_Kind_Poly_Call_Type);
+    pct->token = call->token;
+    pct->__unused = call->next;
+    pct->callee = (AstType *) call->callee;
+    pct->params = (AstNode **) call->args.values;
+    bh_arr_each(AstNode *, pp, pct->params) {
+        if ((*pp)->kind == Ast_Kind_Argument) {
+            *pp = (AstNode *) (*(AstArgument **) pp)->value;
+        }
+    }
+
+    return pct;
+}
diff --git a/compiler/src/builtins.c b/compiler/src/builtins.c
new file mode 100644 (file)
index 0000000..b6eacef
--- /dev/null
@@ -0,0 +1,555 @@
+#include "astnodes.h"
+#include "types.h"
+#include "errors.h"
+#include "utils.h"
+
+AstBasicType basic_type_void      = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_Void]  };
+AstBasicType basic_type_bool      = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_Bool]  };
+AstBasicType basic_type_i8        = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_I8]    };
+AstBasicType basic_type_u8        = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_U8]    };
+AstBasicType basic_type_i16       = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_I16]   };
+AstBasicType basic_type_u16       = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_U16]   };
+AstBasicType basic_type_i32       = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_I32]   };
+AstBasicType basic_type_u32       = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_U32]   };
+AstBasicType basic_type_i64       = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_I64]   };
+AstBasicType basic_type_u64       = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_U64]   };
+AstBasicType basic_type_f32       = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_F32]   };
+AstBasicType basic_type_f64       = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_F64]   };
+AstBasicType basic_type_rawptr    = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_Rawptr] };
+AstBasicType basic_type_type_expr = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_Type_Index] };
+
+// NOTE: Types used for numeric literals
+AstBasicType basic_type_int_unsized   = { Ast_Kind_Basic_Type, 0, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_Int_Unsized] };
+AstBasicType basic_type_float_unsized = { Ast_Kind_Basic_Type, 0, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_Float_Unsized] };
+
+static OnyxToken simd_token = { Token_Type_Symbol, 0, "", { 0 } };
+AstBasicType basic_type_i8x16 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, &simd_token, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_I8X16] };
+AstBasicType basic_type_i16x8 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, &simd_token, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_I16X8] };
+AstBasicType basic_type_i32x4 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, &simd_token, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_I32X4] };
+AstBasicType basic_type_i64x2 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, &simd_token, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_I64X2] };
+AstBasicType basic_type_f32x4 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, &simd_token, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_F32X4] };
+AstBasicType basic_type_f64x2 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, &simd_token, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_F64X2] };
+AstBasicType basic_type_v128  = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, &simd_token, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_V128]  };
+
+// HACK
+// :AutoReturnType
+Type         type_auto_return = { 0 };
+AstBasicType basic_type_auto_return = { Ast_Kind_Basic_Type, 0, &simd_token, NULL, NULL, 0, NULL, &type_auto_return };
+
+OnyxToken builtin_package_token = { Token_Type_Symbol, 7, "builtin ", { 0 } };
+
+static OnyxToken builtin_heap_start_token = { Token_Type_Symbol, 12, "__heap_start ", { 0 } };
+static OnyxToken builtin_stack_top_token  = { Token_Type_Symbol, 11, "__stack_top ",  { 0 } };
+static OnyxToken builtin_tls_base_token   = { Token_Type_Symbol, 10, "__tls_base ",  { 0 } };
+static OnyxToken builtin_tls_size_token   = { Token_Type_Symbol, 10, "__tls_size ",  { 0 } };
+AstGlobal builtin_heap_start  = { Ast_Kind_Global, Ast_Flag_Const, &builtin_heap_start_token, NULL, NULL, (AstType *) &basic_type_rawptr, NULL };
+AstGlobal builtin_stack_top   = { Ast_Kind_Global, 0, &builtin_stack_top_token, NULL, NULL, (AstType *) &basic_type_rawptr, NULL };
+AstGlobal builtin_tls_base    = { Ast_Kind_Global, 0, &builtin_tls_base_token, NULL, NULL, (AstType *) &basic_type_rawptr, NULL };
+AstGlobal builtin_tls_size    = { Ast_Kind_Global, 0, &builtin_tls_size_token, NULL, NULL, (AstType *) &basic_type_u32, NULL };
+
+AstType  *builtin_string_type;
+AstType  *builtin_cstring_type;
+AstType  *builtin_range_type;
+Type     *builtin_range_type_type;
+AstType  *builtin_vararg_type;
+Type     *builtin_vararg_type_type;
+AstTyped *builtin_context_variable;
+AstType  *builtin_allocator_type;
+AstType  *builtin_iterator_type;
+AstType  *builtin_callsite_type;
+AstType  *builtin_any_type;
+AstType  *builtin_code_type;
+AstType  *builtin_link_options_type;
+
+AstTyped    *type_table_node = NULL;
+AstTyped    *foreign_blocks_node = NULL;
+AstType     *foreign_block_type = NULL;
+AstTyped    *tagged_procedures_node = NULL;
+AstFunction *builtin_initialize_data_segments = NULL;
+AstFunction *builtin_run_init_procedures = NULL;
+bh_arr(AstFunction *) init_procedures = NULL;
+
+const BuiltinSymbol builtin_symbols[] = {
+    { NULL, "void",       (AstNode *) &basic_type_void },
+    { NULL, "bool",       (AstNode *) &basic_type_bool },
+    { NULL, "i8",         (AstNode *) &basic_type_i8 },
+    { NULL, "u8",         (AstNode *) &basic_type_u8 },
+    { NULL, "i16",        (AstNode *) &basic_type_i16 },
+    { NULL, "u16",        (AstNode *) &basic_type_u16 },
+    { NULL, "i32",        (AstNode *) &basic_type_i32 },
+    { NULL, "u32",        (AstNode *) &basic_type_u32 },
+    { NULL, "i64",        (AstNode *) &basic_type_i64 },
+    { NULL, "u64",        (AstNode *) &basic_type_u64 },
+    { NULL, "f32",        (AstNode *) &basic_type_f32 },
+    { NULL, "f64",        (AstNode *) &basic_type_f64 },
+    { NULL, "rawptr",     (AstNode *) &basic_type_rawptr },
+    { NULL, "type_expr",  (AstNode *) &basic_type_type_expr },
+
+    { "simd", "i8x16",    (AstNode *) &basic_type_i8x16 },
+    { "simd", "i16x8",    (AstNode *) &basic_type_i16x8 },
+    { "simd", "i32x4",    (AstNode *) &basic_type_i32x4 },
+    { "simd", "i64x2",    (AstNode *) &basic_type_i64x2 },
+    { "simd", "f32x4",    (AstNode *) &basic_type_f32x4 },
+    { "simd", "f64x2",    (AstNode *) &basic_type_f64x2 },
+    { "simd", "v128",     (AstNode *) &basic_type_v128  },
+
+    { "builtin", "__heap_start", (AstNode *) &builtin_heap_start },
+    { "builtin", "__stack_top",  (AstNode *) &builtin_stack_top },
+    { "builtin", "__tls_base",   (AstNode *) &builtin_tls_base },
+    { "builtin", "__tls_size",   (AstNode *) &builtin_tls_size },
+
+    { NULL, NULL, NULL },
+};
+
+IntrinsicTable intrinsic_table;
+
+static IntrinsicMap builtin_intrinsics[] = {
+    { "memory_size",  ONYX_INTRINSIC_MEMORY_SIZE },
+    { "memory_grow",  ONYX_INTRINSIC_MEMORY_GROW },
+    { "memory_copy",  ONYX_INTRINSIC_MEMORY_COPY },
+    { "memory_fill",  ONYX_INTRINSIC_MEMORY_FILL },
+
+    { "__initialize", ONYX_INTRINSIC_INITIALIZE },
+
+    { "clz_i32",      ONYX_INTRINSIC_I32_CLZ },
+    { "ctz_i32",      ONYX_INTRINSIC_I32_CTZ },
+    { "popcnt_i32",   ONYX_INTRINSIC_I32_POPCNT },
+    { "and_i32",      ONYX_INTRINSIC_I32_AND },
+    { "or_i32",       ONYX_INTRINSIC_I32_OR },
+    { "xor_i32",      ONYX_INTRINSIC_I32_XOR },
+    { "shl_i32",      ONYX_INTRINSIC_I32_SHL },
+    { "slr_i32",      ONYX_INTRINSIC_I32_SLR },
+    { "sar_i32",      ONYX_INTRINSIC_I32_SAR },
+    { "rotl_i32",     ONYX_INTRINSIC_I32_ROTL },
+    { "rotr_i32",     ONYX_INTRINSIC_I32_ROTR },
+
+    { "clz_i64",      ONYX_INTRINSIC_I64_CLZ },
+    { "ctz_i64",      ONYX_INTRINSIC_I64_CTZ },
+    { "popcnt_i64",   ONYX_INTRINSIC_I64_POPCNT },
+    { "and_i64",      ONYX_INTRINSIC_I64_AND },
+    { "or_i64",       ONYX_INTRINSIC_I64_OR },
+    { "xor_i64",      ONYX_INTRINSIC_I64_XOR },
+    { "shl_i64",      ONYX_INTRINSIC_I64_SHL },
+    { "slr_i64",      ONYX_INTRINSIC_I64_SLR },
+    { "sar_i64",      ONYX_INTRINSIC_I64_SAR },
+    { "rotl_i64",     ONYX_INTRINSIC_I64_ROTL },
+    { "rotr_i64",     ONYX_INTRINSIC_I64_ROTR },
+
+    { "abs_f32",      ONYX_INTRINSIC_F32_ABS },
+    { "ceil_f32",     ONYX_INTRINSIC_F32_CEIL },
+    { "floor_f32",    ONYX_INTRINSIC_F32_FLOOR },
+    { "trunc_f32",    ONYX_INTRINSIC_F32_TRUNC },
+    { "nearest_f32",  ONYX_INTRINSIC_F32_NEAREST },
+    { "sqrt_f32",     ONYX_INTRINSIC_F32_SQRT },
+    { "min_f32",      ONYX_INTRINSIC_F32_MIN },
+    { "max_f32",      ONYX_INTRINSIC_F32_MAX },
+    { "copysign_f32", ONYX_INTRINSIC_F32_COPYSIGN },
+
+    { "abs_f64",      ONYX_INTRINSIC_F64_ABS },
+    { "ceil_f64",     ONYX_INTRINSIC_F64_CEIL },
+    { "floor_f64",    ONYX_INTRINSIC_F64_FLOOR },
+    { "trunc_f64",    ONYX_INTRINSIC_F64_TRUNC },
+    { "nearest_f64",  ONYX_INTRINSIC_F64_NEAREST },
+    { "sqrt_f64",     ONYX_INTRINSIC_F64_SQRT },
+    { "min_f64",      ONYX_INTRINSIC_F64_MIN },
+    { "max_f64",      ONYX_INTRINSIC_F64_MAX },
+    { "copysign_f64", ONYX_INTRINSIC_F64_COPYSIGN },
+
+
+    // SIMD Intrinsics
+    { "v128_const", ONYX_INTRINSIC_V128_CONST },
+    { "i8x16_const", ONYX_INTRINSIC_I8X16_CONST },
+    { "i16x8_const", ONYX_INTRINSIC_I16X8_CONST },
+    { "i32x4_const", ONYX_INTRINSIC_I32X4_CONST },
+    { "i64x2_const", ONYX_INTRINSIC_I64X2_CONST },
+    { "f32x4_const", ONYX_INTRINSIC_F32X4_CONST },
+    { "f64x2_const", ONYX_INTRINSIC_F64X2_CONST },
+    { "i8x16_shuffle", ONYX_INTRINSIC_I8X16_SHUFFLE },
+
+    { "i8x16_extract_lane_s", ONYX_INTRINSIC_I8X16_EXTRACT_LANE_S },
+    { "i8x16_extract_lane_u", ONYX_INTRINSIC_I8X16_EXTRACT_LANE_U },
+       { "i8x16_replace_lane",   ONYX_INTRINSIC_I8X16_REPLACE_LANE },
+    { "i16x8_extract_lane_s", ONYX_INTRINSIC_I16X8_EXTRACT_LANE_S },
+       { "i16x8_extract_lane_u", ONYX_INTRINSIC_I16X8_EXTRACT_LANE_U },
+       { "i16x8_replace_lane",   ONYX_INTRINSIC_I16X8_REPLACE_LANE },
+       { "i32x4_extract_lane",   ONYX_INTRINSIC_I32X4_EXTRACT_LANE },
+       { "i32x4_replace_lane",   ONYX_INTRINSIC_I32X4_REPLACE_LANE },
+    { "i64x2_extract_lane",   ONYX_INTRINSIC_I64X2_EXTRACT_LANE },
+       { "i64x2_replace_lane",   ONYX_INTRINSIC_I64X2_REPLACE_LANE },
+       { "f32x4_extract_lane",   ONYX_INTRINSIC_F32X4_EXTRACT_LANE },
+       { "f32x4_replace_lane",   ONYX_INTRINSIC_F32X4_REPLACE_LANE },
+       { "f64x2_extract_lane",   ONYX_INTRINSIC_F64X2_EXTRACT_LANE },
+       { "f64x2_replace_lane",   ONYX_INTRINSIC_F64X2_REPLACE_LANE },
+
+    { "i8x16_swizzle", ONYX_INTRINSIC_I8X16_SWIZZLE },
+    { "i8x16_splat",   ONYX_INTRINSIC_I8X16_SPLAT },
+       { "i16x8_splat",   ONYX_INTRINSIC_I16X8_SPLAT },
+    { "i32x4_splat",   ONYX_INTRINSIC_I32X4_SPLAT },
+       { "i64x2_splat",   ONYX_INTRINSIC_I64X2_SPLAT },
+    { "f32x4_splat",   ONYX_INTRINSIC_F32X4_SPLAT },
+       { "f64x2_splat",   ONYX_INTRINSIC_F64X2_SPLAT },
+
+    { "i8x16_eq",   ONYX_INTRINSIC_I8X16_EQ },
+       { "i8x16_neq",  ONYX_INTRINSIC_I8X16_NEQ },
+    { "i8x16_lt_s", ONYX_INTRINSIC_I8X16_LT_S },
+       { "i8x16_lt_u", ONYX_INTRINSIC_I8X16_LT_U },
+    { "i8x16_gt_s", ONYX_INTRINSIC_I8X16_GT_S },
+       { "i8x16_gt_u", ONYX_INTRINSIC_I8X16_GT_U },
+    { "i8x16_le_s", ONYX_INTRINSIC_I8X16_LE_S },
+       { "i8x16_le_u", ONYX_INTRINSIC_I8X16_LE_U },
+    { "i8x16_ge_s", ONYX_INTRINSIC_I8X16_GE_S },
+       { "i8x16_ge_u", ONYX_INTRINSIC_I8X16_GE_U },
+
+    { "i16x8_eq",   ONYX_INTRINSIC_I16X8_EQ },
+       { "i16x8_neq",  ONYX_INTRINSIC_I16X8_NEQ },
+    { "i16x8_lt_s", ONYX_INTRINSIC_I16X8_LT_S },
+       { "i16x8_lt_u", ONYX_INTRINSIC_I16X8_LT_U },
+    { "i16x8_gt_s", ONYX_INTRINSIC_I16X8_GT_S },
+       { "i16x8_gt_u", ONYX_INTRINSIC_I16X8_GT_U },
+    { "i16x8_le_s", ONYX_INTRINSIC_I16X8_LE_S },
+       { "i16x8_le_u", ONYX_INTRINSIC_I16X8_LE_U },
+    { "i16x8_ge_s", ONYX_INTRINSIC_I16X8_GE_S },
+       { "i16x8_ge_u", ONYX_INTRINSIC_I16X8_GE_U },
+
+    { "i32x4_eq",   ONYX_INTRINSIC_I32X4_EQ },
+       { "i32x4_neq",  ONYX_INTRINSIC_I32X4_NEQ },
+    { "i32x4_lt_s", ONYX_INTRINSIC_I32X4_LT_S },
+       { "i32x4_lt_u", ONYX_INTRINSIC_I32X4_LT_U },
+    { "i32x4_gt_s", ONYX_INTRINSIC_I32X4_GT_S },
+       { "i32x4_gt_u", ONYX_INTRINSIC_I32X4_GT_U },
+    { "i32x4_le_s", ONYX_INTRINSIC_I32X4_LE_S },
+       { "i32x4_le_u", ONYX_INTRINSIC_I32X4_LE_U },
+    { "i32x4_ge_s", ONYX_INTRINSIC_I32X4_GE_S },
+       { "i32x4_ge_u", ONYX_INTRINSIC_I32X4_GE_U },
+
+    { "f32x4_eq",  ONYX_INTRINSIC_F32X4_EQ },
+       { "f32x4_neq", ONYX_INTRINSIC_F32X4_NEQ },
+    { "f32x4_lt",  ONYX_INTRINSIC_F32X4_LT },
+       { "f32x4_gt",  ONYX_INTRINSIC_F32X4_GT },
+    { "f32x4_le",  ONYX_INTRINSIC_F32X4_LE },
+       { "f32x4_ge",  ONYX_INTRINSIC_F32X4_GE },
+
+    { "f64x2_eq",  ONYX_INTRINSIC_F64X2_EQ },
+       { "f64x2_neq", ONYX_INTRINSIC_F64X2_NEQ },
+    { "f64x2_lt",  ONYX_INTRINSIC_F64X2_LT },
+       { "f64x2_gt",  ONYX_INTRINSIC_F64X2_GT },
+    { "f64x2_le",  ONYX_INTRINSIC_F64X2_LE },
+       { "f64x2_ge",  ONYX_INTRINSIC_F64X2_GE },
+
+    { "v128_not",       ONYX_INTRINSIC_V128_NOT },
+       { "v128_and",       ONYX_INTRINSIC_V128_AND },
+       { "v128_andnot",    ONYX_INTRINSIC_V128_ANDNOT },
+    { "v128_or",        ONYX_INTRINSIC_V128_OR },
+       { "v128_xor",       ONYX_INTRINSIC_V128_XOR },
+       { "v128_bitselect", ONYX_INTRINSIC_V128_BITSELECT },
+
+    { "i8x16_abs",            ONYX_INTRINSIC_I8X16_ABS },
+       { "i8x16_neg",            ONYX_INTRINSIC_I8X16_NEG },
+    { "i8x16_any_true",       ONYX_INTRINSIC_I8X16_ANY_TRUE },
+       { "i8x16_all_true",       ONYX_INTRINSIC_I8X16_ALL_TRUE },
+    { "i8x16_bitmask",        ONYX_INTRINSIC_I8X16_BITMASK },
+    { "i8x16_narrow_i16x8_s", ONYX_INTRINSIC_I8X16_NARROW_I16X8_S },
+       { "i8x16_narrow_i16x8_u", ONYX_INTRINSIC_I8X16_NARROW_I16X8_U },
+    { "i8x16_shl",            ONYX_INTRINSIC_I8X16_SHL },
+       { "i8x16_shr_s",          ONYX_INTRINSIC_I8X16_SHR_S },
+       { "i8x16_shr_u",          ONYX_INTRINSIC_I8X16_SHR_U },
+    { "i8x16_add",            ONYX_INTRINSIC_I8X16_ADD },
+       { "i8x16_add_sat_s",      ONYX_INTRINSIC_I8X16_ADD_SAT_S },
+       { "i8x16_add_sat_u",      ONYX_INTRINSIC_I8X16_ADD_SAT_U },
+    { "i8x16_sub",            ONYX_INTRINSIC_I8X16_SUB },
+       { "i8x16_sub_sat_s",      ONYX_INTRINSIC_I8X16_SUB_SAT_S },
+       { "i8x16_sub_sat_u",      ONYX_INTRINSIC_I8X16_SUB_SAT_U },
+    { "i8x16_min_s",          ONYX_INTRINSIC_I8X16_MIN_S },
+       { "i8x16_min_u",          ONYX_INTRINSIC_I8X16_MIN_U },
+    { "i8x16_max_s",          ONYX_INTRINSIC_I8X16_MAX_S },
+       { "i8x16_max_u",          ONYX_INTRINSIC_I8X16_MAX_U },
+    { "i8x16_avgr_u",         ONYX_INTRINSIC_I8X16_AVGR_U },
+
+    { "i16x8_abs",                ONYX_INTRINSIC_I16X8_ABS },
+       { "i16x8_neg",                ONYX_INTRINSIC_I16X8_NEG },
+    { "i16x8_any_true",           ONYX_INTRINSIC_I16X8_ANY_TRUE },
+       { "i16x8_all_true",           ONYX_INTRINSIC_I16X8_ALL_TRUE },
+    { "i16x8_bitmask",            ONYX_INTRINSIC_I16X8_BITMASK },
+    { "i16x8_narrow_i32x4_s",     ONYX_INTRINSIC_I16X8_NARROW_I32X4_S },
+       { "i16x8_narrow_i32x4_u",     ONYX_INTRINSIC_I16X8_NARROW_I32X4_U },
+    { "i16x8_widen_low_i8x16_s",  ONYX_INTRINSIC_I16X8_WIDEN_LOW_I8X16_S },
+       { "i16x8_widen_high_i8x16_s", ONYX_INTRINSIC_I16X8_WIDEN_HIGH_I8X16_S },
+    { "i16x8_widen_low_i8x16_u",  ONYX_INTRINSIC_I16X8_WIDEN_LOW_I8X16_U },
+       { "i16x8_widen_high_i8x16_u", ONYX_INTRINSIC_I16X8_WIDEN_HIGH_I8X16_U },
+    { "i16x8_shl",                ONYX_INTRINSIC_I16X8_SHL },
+       { "i16x8_shr_s",              ONYX_INTRINSIC_I16X8_SHR_S },
+       { "i16x8_shr_u",              ONYX_INTRINSIC_I16X8_SHR_U },
+    { "i16x8_add",                ONYX_INTRINSIC_I16X8_ADD },
+       { "i16x8_add_sat_s",          ONYX_INTRINSIC_I16X8_ADD_SAT_S },
+       { "i16x8_add_sat_u",          ONYX_INTRINSIC_I16X8_ADD_SAT_U },
+    { "i16x8_sub",                ONYX_INTRINSIC_I16X8_SUB },
+       { "i16x8_sub_sat_s",          ONYX_INTRINSIC_I16X8_SUB_SAT_S },
+       { "i16x8_sub_sat_u",          ONYX_INTRINSIC_I16X8_SUB_SAT_U },
+    { "i16x8_mul",                ONYX_INTRINSIC_I16X8_MUL },
+    { "i16x8_min_s",              ONYX_INTRINSIC_I16X8_MIN_S },
+       { "i16x8_min_u",              ONYX_INTRINSIC_I16X8_MIN_U },
+    { "i16x8_max_s",              ONYX_INTRINSIC_I16X8_MAX_S },
+       { "i16x8_max_u",              ONYX_INTRINSIC_I16X8_MAX_U },
+    { "i16x8_avgr_u",             ONYX_INTRINSIC_I16X8_AVGR_U },
+
+    { "i32x4_abs",                ONYX_INTRINSIC_I32X4_ABS },
+       { "i32x4_neg",                ONYX_INTRINSIC_I32X4_NEG },
+    { "i32x4_any_true",           ONYX_INTRINSIC_I32X4_ANY_TRUE },
+       { "i32x4_all_true",           ONYX_INTRINSIC_I32X4_ALL_TRUE },
+    { "i32x4_bitmask",            ONYX_INTRINSIC_I32X4_BITMASK },
+    { "i32x4_widen_low_i16x8_s",  ONYX_INTRINSIC_I32X4_WIDEN_LOW_I16X8_S },
+       { "i32x4_widen_high_i16x8_s", ONYX_INTRINSIC_I32X4_WIDEN_HIGH_I16X8_S },
+    { "i32x4_widen_low_i16x8_u",  ONYX_INTRINSIC_I32X4_WIDEN_LOW_I16X8_U },
+       { "i32x4_widen_high_i16x8_u", ONYX_INTRINSIC_I32X4_WIDEN_HIGH_I16X8_U },
+    { "i32x4_shl",                ONYX_INTRINSIC_I32X4_SHL },
+       { "i32x4_shr_s",              ONYX_INTRINSIC_I32X4_SHR_S },
+       { "i32x4_shl_u",              ONYX_INTRINSIC_I32X4_SHR_U },
+    { "i32x4_add",                ONYX_INTRINSIC_I32X4_ADD },
+       { "i32x4_sub",                ONYX_INTRINSIC_I32X4_SUB },
+       { "i32x4_mul",                ONYX_INTRINSIC_I32X4_MUL },
+    { "i32x4_min_s",              ONYX_INTRINSIC_I32X4_MIN_S },
+       { "i32x4_min_u",              ONYX_INTRINSIC_I32X4_MIN_U },
+    { "i32x4_max_s",              ONYX_INTRINSIC_I32X4_MAX_S },
+       { "i32x4_max_u",              ONYX_INTRINSIC_I32X4_MAX_U },
+
+    { "i64x2_neg",   ONYX_INTRINSIC_I64X2_NEG },
+       { "i64x2_shl",   ONYX_INTRINSIC_I64X2_SHL },
+    { "i64x2_shr_s", ONYX_INTRINSIC_I64X2_SHR_S },
+       { "i64x2_shr_u", ONYX_INTRINSIC_I64X2_SHR_U },
+    { "i64x2_add",   ONYX_INTRINSIC_I64X2_ADD },
+       { "i64x2_sub",   ONYX_INTRINSIC_I64X2_SUB },
+       { "i64x2_mul",   ONYX_INTRINSIC_I64X2_MUL },
+
+    { "f32x4_abs",  ONYX_INTRINSIC_F32X4_ABS },
+       { "f32x4_neg",  ONYX_INTRINSIC_F32X4_NEG },
+       { "f32x4_sqrt", ONYX_INTRINSIC_F32X4_SQRT },
+    { "f32x4_add",  ONYX_INTRINSIC_F32X4_ADD },
+       { "f32x4_sub",  ONYX_INTRINSIC_F32X4_SUB },
+    { "f32x4_mul",  ONYX_INTRINSIC_F32X4_MUL },
+       { "f32x4_div",  ONYX_INTRINSIC_F32X4_DIV },
+    { "f32x4_min",  ONYX_INTRINSIC_F32X4_MIN },
+       { "f32x4_max",  ONYX_INTRINSIC_F32X4_MAX },
+
+    { "f64x2_abs",  ONYX_INTRINSIC_F64X2_ABS },
+       { "f64x2_neg",  ONYX_INTRINSIC_F64X2_NEG },
+       { "f64x2_sqrt", ONYX_INTRINSIC_F64X2_SQRT },
+    { "f64x2_add",  ONYX_INTRINSIC_F64X2_ADD },
+       { "f64x2_sub",  ONYX_INTRINSIC_F64X2_SUB },
+    { "f64x2_mul",  ONYX_INTRINSIC_F64X2_MUL },
+       { "f64x2_div",  ONYX_INTRINSIC_F64X2_DIV },
+    { "f64x2_min",  ONYX_INTRINSIC_F64X2_MIN },
+       { "f64x2_max",  ONYX_INTRINSIC_F64X2_MAX },
+
+    { "i32x4_trunc_sat_f32x4_s", ONYX_INTRINSIC_I32X4_TRUNC_SAT_F32X4_S },
+    { "i32x4_trunc_sat_f32x4_u", ONYX_INTRINSIC_I32X4_TRUNC_SAT_F32X4_U },
+    { "f32x4_convert_i32x4_s",   ONYX_INTRINSIC_F32X4_CONVERT_I32X4_S },
+    { "f32x4_convert_i32x4_u",   ONYX_INTRINSIC_F32X4_CONVERT_I32X4_U },
+
+    { "__atomic_wait",    ONYX_INTRINSIC_ATOMIC_WAIT    },
+    { "__atomic_notify",  ONYX_INTRINSIC_ATOMIC_NOTIFY  },
+
+    { "__atomic_fence",   ONYX_INTRINSIC_ATOMIC_FENCE   },
+
+    { "__atomic_load",    ONYX_INTRINSIC_ATOMIC_LOAD    },
+    { "__atomic_store",   ONYX_INTRINSIC_ATOMIC_STORE   },
+    { "__atomic_add",     ONYX_INTRINSIC_ATOMIC_ADD     },
+    { "__atomic_sub",     ONYX_INTRINSIC_ATOMIC_SUB     },
+    { "__atomic_and",     ONYX_INTRINSIC_ATOMIC_AND     },
+    { "__atomic_or",      ONYX_INTRINSIC_ATOMIC_OR      },
+    { "__atomic_xor",     ONYX_INTRINSIC_ATOMIC_XOR     },
+    { "__atomic_xchg",    ONYX_INTRINSIC_ATOMIC_XCHG    },
+    { "__atomic_cmpxchg", ONYX_INTRINSIC_ATOMIC_CMPXCHG },
+
+    { NULL, ONYX_INTRINSIC_UNDEFINED },
+};
+
+bh_arr(OverloadOption) operator_overloads[Binary_Op_Count] = { 0 };
+
+void initialize_builtins(bh_allocator a) {
+    // HACK
+    builtin_package_token.text = bh_strdup(global_heap_allocator, builtin_package_token.text);
+
+    BuiltinSymbol* bsym = (BuiltinSymbol *) &builtin_symbols[0];
+    while (bsym->sym != NULL) {
+        if (bsym->package == NULL)
+            symbol_builtin_introduce(context.global_scope, bsym->sym, bsym->node);
+        else {
+            Package* p = package_lookup_or_create(bsym->package, context.global_scope, a, context.global_scope->created_at);
+            assert(p);
+
+            symbol_builtin_introduce(p->scope, bsym->sym, bsym->node);
+        }
+        bsym++;
+    }
+
+    Package* p = package_lookup_or_create("builtin", context.global_scope, a, context.global_scope->created_at);
+
+    builtin_string_type = (AstType *) symbol_raw_resolve(p->scope, "str");
+    if (builtin_string_type == NULL) {
+        onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'str' struct not found in builtin package.");
+        return;
+    }
+
+    builtin_cstring_type = (AstType *) symbol_raw_resolve(p->scope, "cstr");
+    if (builtin_cstring_type == NULL) {
+        onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'cstr' type not found in builtin package.");
+        return;
+    }
+
+    builtin_range_type = (AstType *) symbol_raw_resolve(p->scope, "range");
+    if (builtin_range_type == NULL) {
+        onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'range' struct not found in builtin package.");
+        return;
+    }
+
+    builtin_vararg_type = (AstType *) symbol_raw_resolve(p->scope, "vararg");
+    if (builtin_range_type == NULL) {
+        onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'vararg' struct not found in builtin package.");
+        return;
+    }
+
+    builtin_context_variable = (AstTyped *) symbol_raw_resolve(p->scope, "context");
+    if (builtin_context_variable == NULL) {
+        onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'context' variable not found in builtin package.");
+        return;
+    }
+
+    builtin_allocator_type = (AstType *) symbol_raw_resolve(p->scope, "Allocator");
+    if (builtin_allocator_type == NULL) {
+        onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'Allocator' struct not found in builtin package.");
+        return;
+    }
+
+    builtin_iterator_type = (AstType *) symbol_raw_resolve(p->scope, "Iterator");
+    if (builtin_iterator_type == NULL) {
+        onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'Iterator' struct not found in builtin package.");
+        return;
+    }
+
+    builtin_callsite_type = (AstType *) symbol_raw_resolve(p->scope, "CallSite");
+    if (builtin_callsite_type == NULL) {
+        onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'CallSite' struct not found in builtin package.");
+        return;
+    }
+
+    builtin_any_type = (AstType *) symbol_raw_resolve(p->scope, "any");
+    if (builtin_any_type == NULL) {
+        onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'any' struct not found in builtin package.");
+        return;
+    }
+
+    builtin_code_type = (AstType *) symbol_raw_resolve(p->scope, "Code");
+    if (builtin_code_type == NULL) {
+        onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'Code' struct not found in builtin package.");
+        return;
+    }
+
+    builtin_initialize_data_segments = (AstFunction *) symbol_raw_resolve(p->scope, "__initialize_data_segments");
+    if (builtin_initialize_data_segments == NULL || builtin_initialize_data_segments->kind != Ast_Kind_Function) {
+        onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'__initialize_data_segments' procedure not found in builtin package.");
+        return;
+    }
+
+    builtin_run_init_procedures = (AstFunction *) symbol_raw_resolve(p->scope, "__run_init_procedures");
+    if (builtin_run_init_procedures == NULL || builtin_run_init_procedures->kind != Ast_Kind_Function) {
+        onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'__run_init_procedures' procedure not found.");
+        return;
+    }
+
+    builtin_link_options_type = (AstType *) symbol_raw_resolve(p->scope, "Link_Options");
+    if (builtin_link_options_type == NULL) {
+        onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'Link_Options' type not found.");
+        return;
+    }
+
+    bh_arr_new(global_heap_allocator, init_procedures, 4);
+
+    fori (i, 0, Binary_Op_Count) {
+        bh_arr_new(global_heap_allocator, operator_overloads[i], 4); 
+    }
+
+    IntrinsicMap* intrinsic = &builtin_intrinsics[0];
+    while (intrinsic->name != NULL) {
+        shput(intrinsic_table, intrinsic->name, intrinsic->intrinsic);
+        intrinsic++;
+    }
+}
+
+void initalize_special_globals() {
+    Package *p = package_lookup("runtime.info");
+    if (p != NULL) {
+        type_table_node     = (AstTyped *) symbol_raw_resolve(p->scope, "type_table");
+        foreign_blocks_node = (AstTyped *) symbol_raw_resolve(p->scope, "foreign_blocks");
+        foreign_block_type  = (AstType *)  symbol_raw_resolve(p->scope, "foreign_block");
+        tagged_procedures_node = (AstTyped *) symbol_raw_resolve(p->scope, "tagged_procedures");
+    }
+}
+
+void introduce_build_options(bh_allocator a) {
+    Package* p = package_lookup_or_create("runtime", context.global_scope, a, context.global_scope->created_at);
+
+    AstType* Runtime_Type = (AstType *) symbol_raw_resolve(p->scope, "Runtime");
+    if (Runtime_Type == NULL) {
+        onyx_report_error((OnyxFilePos) {0}, Error_Critical, "'Runtime' type not found in package runtime.");
+        return;
+    }
+
+    AstNumLit* runtime_type = make_int_literal(a, context.options->runtime);
+    runtime_type->type_node = Runtime_Type;
+    add_entities_for_node(NULL, (AstNode *) runtime_type, NULL, NULL);
+    symbol_builtin_introduce(p->scope, "runtime", (AstNode *) runtime_type);
+
+    AstNumLit* multi_threaded = make_int_literal(a, context.options->use_multi_threading);
+    multi_threaded->type_node = (AstType *) &basic_type_bool;
+    symbol_builtin_introduce(p->scope, "Multi_Threading_Enabled", (AstNode *) multi_threaded);
+
+    AstNumLit* wait_notify_available = make_int_literal(a, context.options->use_multi_threading && context.options->runtime == Runtime_Js);
+    wait_notify_available->type_node = (AstType *) &basic_type_bool;
+    symbol_builtin_introduce(p->scope, "Wait_Notify_Available", (AstNode *) wait_notify_available);
+
+    i32 os;
+    #ifdef _BH_LINUX
+        os = 1;
+    #endif
+    #ifdef _BH_WINDOWS
+        os = 2;
+    #endif
+
+    AstType* OS_Type = (AstType *) symbol_raw_resolve(p->scope, "OS");
+    if (OS_Type == NULL) {
+        onyx_report_error((OnyxFilePos) {0}, Error_Critical, "'OS' type not found in package runtime.");
+        return;
+    }
+
+    AstNumLit* os_type = make_int_literal(a, os);
+    os_type->type_node = OS_Type;
+    add_entities_for_node(NULL, (AstNode *) os_type, NULL, NULL);
+    symbol_builtin_introduce(p->scope, "compiler_os", (AstNode *) os_type);
+
+    i32 arch = 0;
+    #if defined(__x86_64__) || defined(_M_X64)
+        arch = 1; // X86_64;
+    #elif defined(i386) || defined(__i386__) || defined(__i386) || defined(_M_IX86)
+        arch = 2; // X86_32;
+    #elif defined(__aarch64__) || defined(_M_ARM64)
+        arch = 3; // AARCH64;
+    #endif
+
+    AstType* Arch_Type = (AstType *) symbol_raw_resolve(p->scope, "Arch");
+    if (Arch_Type == NULL) {
+        onyx_report_error((OnyxFilePos) {0}, Error_Critical, "'Arch' type not found in package runtime.");
+        return;
+    }
+
+    AstNumLit* arch_type = make_int_literal(a, arch);
+    arch_type->type_node = Arch_Type;
+    add_entities_for_node(NULL, (AstNode *) arch_type, NULL, NULL);
+    symbol_builtin_introduce(p->scope, "arch", (AstNode *) arch_type);
+
+    if (context.options->generate_foreign_info) {
+        AstNumLit* foreign_info = make_int_literal(a, 1);
+        foreign_info->type_node = (AstType *) &basic_type_bool;
+        symbol_builtin_introduce(p->scope, "Generated_Foreign_Info", (AstNode *) foreign_info);
+    }
+}
+
diff --git a/compiler/src/checker.c b/compiler/src/checker.c
new file mode 100644 (file)
index 0000000..333e331
--- /dev/null
@@ -0,0 +1,3023 @@
+#define BH_DEBUG
+#include "parser.h"
+#include "utils.h"
+
+// All of the `check` functions return a boolean that signals if an issue
+// was reached while processing the node. These error booleans propagate
+// up the call stack until they reach `check_entity`.
+
+#define CHECK(kind, ...) do { \
+    CheckStatus cs = check_ ## kind (__VA_ARGS__); \
+    if (cs > Check_Errors_Start) return cs; \
+    } while (0)
+
+#define YIELD(loc, msg) do { \
+    if (context.cycle_detected) { \
+        onyx_report_error(loc, Error_Waiting_On, msg); \
+        return Check_Error; \
+    } else { \
+        return Check_Yield_Macro; \
+    } \
+    } while (0)
+
+#define YIELD_(loc, msg, ...) do { \
+    if (context.cycle_detected) { \
+        onyx_report_error(loc, Error_Waiting_On, msg, __VA_ARGS__); \
+        return Check_Error; \
+    } else { \
+        return Check_Yield_Macro; \
+    } \
+    } while (0)
+
+#define YIELD_ERROR(loc, msg) do { \
+    if (context.cycle_detected) { \
+        onyx_report_error(loc, Error_Critical, msg); \
+        return Check_Error; \
+    } else { \
+        return Check_Yield_Macro; \
+    } \
+    } while (0)
+
+#define ERROR(loc, msg) do { \
+    onyx_report_error(loc, Error_Critical, msg); \
+    return Check_Error; \
+    } while (0)
+
+#define ERROR_(loc, msg, ...) do { \
+    onyx_report_error(loc, Error_Critical, msg, __VA_ARGS__); \
+    return Check_Error; \
+    } while (0)
+
+#define TYPE_CHECK_(expr, type, type_name)                                                      \
+    TypeMatch type_name;                                                                        \
+    type_name = unify_node_and_type(expr, type);                                                \
+    if (type_name == TYPE_MATCH_YIELD) YIELD((*expr)->token->pos, "Waiting on type checking."); \
+    if (type_name == TYPE_MATCH_FAILED)
+
+#define CONCAT(a, b) a##_##b
+#define DEFER_LINE(a, line) CONCAT(a, line)
+#define TYPE_CHECK(expr, type) TYPE_CHECK_(expr, type, DEFER_LINE(tc, __LINE__))
+
+typedef enum CheckStatus {
+    Check_Success,  // The node was successfully checked with out errors
+    Check_Complete, // The node is done processing
+
+    Check_Errors_Start,
+    Check_Return_To_Symres, // Return this node for further symres processing
+    Check_Yield_Macro,
+    Check_Failed,           // The node is done processing and should be put in the state of Failed.
+    Check_Error,    // There was an error when checking the node
+} CheckStatus;
+
+CheckStatus check_block(AstBlock* block);
+CheckStatus check_statement_chain(AstNode** start);
+CheckStatus check_statement(AstNode** pstmt);
+CheckStatus check_return(AstReturn* retnode);
+CheckStatus check_if(AstIfWhile* ifnode);
+CheckStatus check_while(AstIfWhile* whilenode);
+CheckStatus check_for(AstFor* fornode);
+CheckStatus check_switch(AstSwitch* switchnode);
+CheckStatus check_call(AstCall** pcall);
+CheckStatus check_binaryop(AstBinaryOp** pbinop);
+CheckStatus check_unaryop(AstUnaryOp** punop);
+CheckStatus check_struct_literal(AstStructLiteral* sl);
+CheckStatus check_array_literal(AstArrayLiteral* al);
+CheckStatus check_range_literal(AstRangeLiteral** range);
+CheckStatus check_compound(AstCompound* compound);
+CheckStatus check_if_expression(AstIfExpression* if_expr);
+CheckStatus check_expression(AstTyped** expr);
+CheckStatus check_address_of(AstAddressOf** paof);
+CheckStatus check_dereference(AstDereference* deref);
+CheckStatus check_subscript(AstSubscript** paa);
+CheckStatus check_field_access(AstFieldAccess** pfield);
+CheckStatus check_method_call(AstBinaryOp** mcall);
+CheckStatus check_size_of(AstSizeOf* so);
+CheckStatus check_align_of(AstAlignOf* ao);
+CheckStatus check_global(AstGlobal* global);
+CheckStatus check_function(AstFunction* func);
+CheckStatus check_overloaded_function(AstOverloadedFunction* func);
+CheckStatus check_struct(AstStructType* s_node);
+CheckStatus check_temp_function_header(AstFunction* func);
+CheckStatus check_function_header(AstFunction* func);
+CheckStatus check_memres_type(AstMemRes* memres);
+CheckStatus check_memres(AstMemRes* memres);
+CheckStatus check_type(AstType** ptype);
+CheckStatus check_insert_directive(AstDirectiveInsert** pinsert);
+CheckStatus check_directive_solidify(AstDirectiveSolidify** psolid);
+CheckStatus check_do_block(AstDoBlock** pdoblock);
+CheckStatus check_constraint(AstConstraint *constraint);
+CheckStatus check_constraint_context(ConstraintContext *cc, Scope *scope, OnyxFilePos pos);
+CheckStatus check_polyquery(AstPolyQuery *query);
+
+// HACK HACK HACK
+b32 expression_types_must_be_known = 0;
+b32 all_checks_are_final           = 1;
+b32 inside_for_iterator            = 0;
+
+#define STATEMENT_LEVEL 1
+#define EXPRESSION_LEVEL 2
+u32 current_checking_level=0;
+
+static inline void fill_in_type(AstTyped* node) {
+    if (node->type == NULL) {
+        if (check_type(&node->type_node) > Check_Errors_Start) return;
+
+        node->type = type_build_from_ast(context.ast_alloc, node->type_node);
+    }
+}
+
+// HACK: This should be baked into a structure, not a global variable.
+static Type** expected_return_type = NULL;
+
+CheckStatus check_return(AstReturn* retnode) {
+    if (retnode->expr) {
+        CHECK(expression, &retnode->expr);
+
+        if (*expected_return_type == &type_auto_return) {
+            resolve_expression_type(retnode->expr);
+            if (retnode->expr->type == NULL)
+                YIELD_ERROR(retnode->token->pos, "Unable to determine the automatic return type here.");
+
+            *expected_return_type = retnode->expr->type;
+            return Check_Success;
+        }
+
+        TYPE_CHECK(&retnode->expr, *expected_return_type) {
+            ERROR_(retnode->token->pos,
+                    "Expected to return a value of type '%s', returning value of type '%s'.",
+                    type_get_name(*expected_return_type),
+                    node_get_type_name(retnode->expr));
+        }
+
+        //
+        // Catch the obvious case of return '^.{ ... }', as that will never
+        // be legal.
+        if (retnode->expr->kind == Ast_Kind_Address_Of) {
+            AstAddressOf *aof = (AstAddressOf *) retnode->expr;
+            if (node_is_addressable_literal((AstNode *) aof->expr)) {
+                ERROR(retnode->token->pos, "Cannot return a pointer to a literal, as the space reserved for the literal will be freed upon returning.");
+            }
+        }
+
+    } else {
+        if (*expected_return_type == &type_auto_return) {
+            *expected_return_type = &basic_types[Basic_Kind_Void];
+            return Check_Success;
+        }
+
+        if ((*expected_return_type)->Basic.size > 0) {
+            ERROR_(retnode->token->pos,
+                "Returning from non-void function without a value. Expected a value of type '%s'.",
+                type_get_name(*expected_return_type));
+        }
+    }
+
+    return Check_Success;
+}
+
+CheckStatus check_if(AstIfWhile* ifnode) {
+    if (ifnode->initialization != NULL) CHECK(statement_chain, &ifnode->initialization);
+
+    if (ifnode->kind == Ast_Kind_Static_If) {
+        if ((ifnode->flags & Ast_Flag_Static_If_Resolved) == 0) {
+            YIELD(ifnode->token->pos, "Waiting for static if to be resolved.");
+        }
+
+        if (static_if_resolution(ifnode)) {
+            if (ifnode->true_stmt != NULL) {
+                CHECK(statement, (AstNode **) &ifnode->true_stmt);
+                ifnode->true_stmt->rules = Block_Rule_Macro;
+            }
+
+        } else {
+            if (ifnode->false_stmt != NULL) {
+                CHECK(statement, (AstNode **) &ifnode->false_stmt);
+                ifnode->false_stmt->rules = Block_Rule_Macro;
+            }
+        }
+
+    } else {
+        CHECK(expression, &ifnode->cond);
+
+        if (!type_is_bool(ifnode->cond->type)) {
+            ERROR_(ifnode->cond->token->pos, "Expected expression of type 'bool' for condition, got '%s'", type_get_name(ifnode->cond->type));
+        }
+
+        if (ifnode->true_stmt)  CHECK(statement, (AstNode **) &ifnode->true_stmt);
+        if (ifnode->false_stmt) CHECK(statement, (AstNode **) &ifnode->false_stmt);
+    }
+
+    return Check_Success;
+}
+
+CheckStatus check_while(AstIfWhile* whilenode) {
+    if (whilenode->initialization != NULL) CHECK(statement_chain, &whilenode->initialization);
+
+    CHECK(expression, &whilenode->cond);
+
+    if (!type_is_bool(whilenode->cond->type)) {
+        ERROR_(whilenode->cond->token->pos, "Expected expression of type 'bool' for condition, got '%s'", type_get_name(whilenode->cond->type));
+    }
+
+    if (whilenode->true_stmt)  CHECK(statement, (AstNode **) &whilenode->true_stmt);
+    if (whilenode->false_stmt) {
+        if (whilenode->bottom_test) {
+            ERROR(whilenode->token->pos, "while-loops with an 'else' clause cannot be bottom tested.");
+        }
+
+        CHECK(statement, (AstNode **) &whilenode->false_stmt);
+    }
+
+    return Check_Success;
+}
+
+CheckStatus check_for(AstFor* fornode) {
+    b32 old_inside_for_iterator;
+    if (fornode->flags & Ast_Flag_Has_Been_Checked) goto fornode_expr_checked;
+
+    CHECK(expression, &fornode->iter);
+    resolve_expression_type(fornode->iter);
+
+    Type* iter_type = fornode->iter->type;
+    if (iter_type == NULL) YIELD(fornode->token->pos, "Waiting for iteration expression type to be known.");
+
+    OnyxFilePos error_loc = fornode->var->token->pos;
+    if (error_loc.filename == NULL) {
+        error_loc = fornode->token->pos;
+    }
+
+    fornode->loop_type = For_Loop_Invalid;
+    if (types_are_compatible(iter_type, &basic_types[Basic_Kind_I32])) {
+        if (fornode->by_pointer) {
+            ERROR(error_loc, "Cannot iterate by pointer over a range.");
+        }
+
+        AstNumLit* low_0    = make_int_literal(context.ast_alloc, 0);
+        AstRangeLiteral* rl = make_range_literal(context.ast_alloc, (AstTyped *) low_0, fornode->iter);
+        CHECK(range_literal, &rl);
+        fornode->iter = (AstTyped *) rl;
+
+        fornode->var->type = builtin_range_type_type->Struct.memarr[0]->type;
+        fornode->var->flags |= Ast_Flag_Cannot_Take_Addr;
+        fornode->loop_type = For_Loop_Range;
+    }
+    else if (types_are_compatible(iter_type, builtin_range_type_type)) {
+        if (fornode->by_pointer) {
+            ERROR(error_loc, "Cannot iterate by pointer over a range.");
+        }
+
+        // NOTE: Blindly copy the first range member's type which will
+        // be the low value.                - brendanfh 2020/09/04
+        fornode->var->type = builtin_range_type_type->Struct.memarr[0]->type;
+        fornode->var->flags |= Ast_Flag_Cannot_Take_Addr;
+        fornode->loop_type = For_Loop_Range;
+
+    }
+    else if (iter_type->kind == Type_Kind_Array) {
+        if (fornode->by_pointer) fornode->var->type = type_make_pointer(context.ast_alloc, iter_type->Array.elem);
+        else                     fornode->var->type = iter_type->Array.elem;
+
+        fornode->loop_type = For_Loop_Array;
+    }
+    else if (iter_type->kind == Type_Kind_Slice) {
+        if (fornode->by_pointer) fornode->var->type = type_make_pointer(context.ast_alloc, iter_type->Slice.elem);
+        else                     fornode->var->type = iter_type->Slice.elem;
+
+        fornode->loop_type = For_Loop_Slice;
+
+    }
+    else if (iter_type->kind == Type_Kind_VarArgs) {
+        if (fornode->by_pointer) {
+            ERROR_(error_loc, "Cannot iterate by pointer over '%s'.", type_get_name(iter_type));
+        }
+
+        fornode->var->type = iter_type->VarArgs.elem;
+
+        // NOTE: Slices are VarArgs are being treated the same here.
+        fornode->loop_type = For_Loop_Slice;
+    }
+    else if (iter_type->kind == Type_Kind_DynArray) {
+        if (fornode->by_pointer) fornode->var->type = type_make_pointer(context.ast_alloc, iter_type->DynArray.elem);
+        else                     fornode->var->type = iter_type->DynArray.elem;
+
+        fornode->loop_type = For_Loop_DynArr;
+    }
+    else if (type_struct_constructed_from_poly_struct(iter_type, builtin_iterator_type)) {
+        if (fornode->by_pointer) {
+            ERROR(error_loc, "Cannot iterate by pointer over an iterator.");
+        }
+
+        // HACK: This assumes the Iterator type only has a single type argument.
+        fornode->var->type = iter_type->Struct.poly_sln[0].type;
+        fornode->loop_type = For_Loop_Iterator;
+    }
+
+    if (fornode->by_pointer)
+        fornode->var->flags |= Ast_Flag_Cannot_Take_Addr;
+
+    if (fornode->loop_type == For_Loop_Invalid)
+        ERROR_(error_loc, "Cannot iterate over a '%s'.", type_get_name(iter_type));
+
+    if (fornode->no_close && fornode->loop_type != For_Loop_Iterator) {
+        onyx_report_warning(error_loc, "Warning: #no_close here is meaningless as the iterable is not an iterator.");
+    }
+
+    fornode->flags |= Ast_Flag_Has_Been_Checked;
+
+fornode_expr_checked:
+    old_inside_for_iterator = inside_for_iterator;
+    inside_for_iterator = 0;
+    iter_type = fornode->iter->type;
+    if (type_struct_constructed_from_poly_struct(iter_type, builtin_iterator_type)) {
+        inside_for_iterator = 1;
+    }
+
+    do {
+        CheckStatus cs = check_block(fornode->stmt);
+        inside_for_iterator = old_inside_for_iterator;
+        if (cs > Check_Errors_Start) return cs; 
+    } while(0);
+
+    return Check_Success;
+}
+
+static b32 add_case_to_switch_statement(AstSwitch* switchnode, u64 case_value, AstBlock* block, OnyxFilePos pos) {
+    assert(switchnode->switch_kind == Switch_Kind_Integer);
+
+    switchnode->min_case = bh_min(switchnode->min_case, case_value);
+    switchnode->max_case = bh_max(switchnode->max_case, case_value);
+
+    if (bh_imap_has(&switchnode->case_map, case_value)) {
+        onyx_report_error(pos, Error_Critical, "Multiple cases for values '%d'.", case_value);
+        return 1;
+    }
+
+    bh_imap_put(&switchnode->case_map, case_value, (u64) block);
+    return 0;
+}
+
+static CheckStatus collect_switch_case_blocks(AstSwitch* switchnode, AstBlock* root) {
+    AstNode *walker = root->body;
+    while (walker != NULL) {
+        switch (walker->kind) {
+            case Ast_Kind_Block:
+                collect_switch_case_blocks(switchnode, (AstBlock *) walker);
+                break;
+
+            case Ast_Kind_Switch_Case: {
+                AstSwitchCase *case_node = (AstSwitchCase *) walker;
+                if (case_node->is_default) {
+                    if (switchnode->default_case != NULL && switchnode->default_case != case_node->block) {
+                        ERROR(case_node->token->pos, "Multiple #default cases given");
+                        ERROR(switchnode->default_case->token->pos, "Multiple #default cases given");
+                        return Check_Error;
+                    }
+
+                    switchnode->default_case = case_node->block;
+                } else {
+                    bh_arr_push(switchnode->cases, case_node);
+                }
+                break;
+            }
+
+            default:
+                ERROR(walker->token->pos, "This statement is not allowed here.");
+        }
+
+        walker = walker->next;
+    }
+
+    return Check_Success;
+}
+
+CheckStatus check_switch(AstSwitch* switchnode) {
+    if (switchnode->initialization != NULL) CHECK(statement_chain, &switchnode->initialization);
+
+    CHECK(expression, &switchnode->expr);
+    Type* resolved_expr_type = resolve_expression_type(switchnode->expr);
+
+    if (!(switchnode->flags & Ast_Flag_Has_Been_Checked)) {
+        if (resolved_expr_type == NULL) YIELD(switchnode->token->pos, "Waiting for expression type to be known.");
+
+        switchnode->switch_kind = Switch_Kind_Integer;
+        if (!type_is_integer(switchnode->expr->type) && switchnode->expr->type->kind != Type_Kind_Enum) {
+            switchnode->switch_kind = Switch_Kind_Use_Equals;
+        }
+
+        switch (switchnode->switch_kind) {
+            case Switch_Kind_Integer:
+                switchnode->min_case = 0xffffffffffffffff;
+                bh_imap_init(&switchnode->case_map, global_heap_allocator, 4);
+                break;
+
+            case Switch_Kind_Use_Equals:
+                bh_arr_new(global_heap_allocator, switchnode->case_exprs, 4);
+                break;
+
+            default: assert(0);
+        }
+    }
+    switchnode->flags |= Ast_Flag_Has_Been_Checked;
+
+    // Should the case block code be checked here?
+    // Or should this just exist to resolve macros and expand #unquotes
+    // then the cases are consumed into the array or cases, THEN the blocks
+    // are actually checked?
+    if (switchnode->cases == NULL) {
+        CHECK(block, switchnode->case_block);
+
+        bh_arr_new(global_heap_allocator, switchnode->cases, 4);
+        if (collect_switch_case_blocks(switchnode, switchnode->case_block) != Check_Success) {
+            return Check_Error;
+        }
+
+        // This is important, otherwise if this block has to return to symbol resolution.
+        switchnode->case_block->statement_idx = 0;
+    }
+
+    fori (i, switchnode->yield_return_index, bh_arr_length(switchnode->cases)) {
+        AstSwitchCase *sc = switchnode->cases[i];
+        CHECK(block, sc->block);
+
+        bh_arr_each(AstTyped *, value, sc->values) {
+            CHECK(expression, value);
+
+            if (switchnode->switch_kind == Switch_Kind_Integer && (*value)->kind == Ast_Kind_Range_Literal) {
+                AstRangeLiteral* rl = (AstRangeLiteral *) (*value);
+                resolve_expression_type(rl->low);
+                resolve_expression_type(rl->high);
+
+                if (rl->low->kind != Ast_Kind_NumLit || rl->high->kind != Ast_Kind_NumLit) {
+                    ERROR(rl->token->pos, "case statement expected compile time known range.");
+                }
+
+                promote_numlit_to_larger((AstNumLit *) rl->low);
+                promote_numlit_to_larger((AstNumLit *) rl->high);
+
+                i64 lower = ((AstNumLit *) rl->low)->value.l;
+                i64 upper = ((AstNumLit *) rl->high)->value.l;
+
+                // NOTE: This is inclusive!!!!
+                fori (case_value, lower, upper + 1) {
+                    if (add_case_to_switch_statement(switchnode, case_value, sc->block, rl->token->pos))
+                        return Check_Error;
+                }
+
+                continue;
+            }
+
+            TYPE_CHECK(value, resolved_expr_type) {
+                OnyxToken* tkn = sc->block->token;
+                if ((*value)->token) tkn = (*value)->token;
+
+                ERROR_(tkn->pos, "Mismatched types in switch-case. Expected '%s', got '%s'.",
+                    type_get_name(resolved_expr_type), type_get_name((*value)->type));
+            }
+
+            switch (switchnode->switch_kind) {
+                case Switch_Kind_Integer: {
+                    b32 is_valid;
+                    i64 integer_value = get_expression_integer_value(*value, &is_valid);
+                    if (!is_valid)
+                        ERROR_((*value)->token->pos, "Case statement expected compile time known integer. Got '%s'.", onyx_ast_node_kind_string((*value)->kind));
+
+                    if (add_case_to_switch_statement(switchnode, integer_value, sc->block, sc->block->token->pos))
+                        return Check_Error;
+
+                    break;
+                }
+
+                case Switch_Kind_Use_Equals: {
+                    // Gross
+                    b32 found = 0;
+                    bh_arr_each(CaseToBlock, ctb, switchnode->case_exprs) {
+                        if (ctb->original_value == *value) {
+                            CHECK(expression, (AstTyped **) &ctb->comparison);
+                            found = 1;
+                            break;
+                        }
+                    }
+                    if (found) break;
+
+                    CaseToBlock ctb;
+                    ctb.block = sc->block;
+                    ctb.original_value = *value;
+                    ctb.comparison = make_binary_op(context.ast_alloc, Binary_Op_Equal, switchnode->expr, *value);
+                    ctb.comparison->token = (*value)->token;
+                    bh_arr_push(switchnode->case_exprs, ctb);
+
+                    CHECK(binaryop, &bh_arr_last(switchnode->case_exprs).comparison);
+                    break;
+                }
+            }
+        }
+
+        switchnode->yield_return_index += 1;
+    }
+
+    if (switchnode->default_case)
+        CHECK(block, switchnode->default_case);
+
+    return 0;
+}
+
+CheckStatus check_arguments(Arguments* args) {
+    bh_arr_each(AstTyped *, actual, args->values)
+        CHECK(expression, actual);
+
+    bh_arr_each(AstNamedValue *, named_value, args->named_values)
+        CHECK(expression, &(*named_value)->value);
+
+    return Check_Success;
+}
+
+CheckStatus check_argument(AstArgument** parg) {
+    CHECK(expression, &(*parg)->value);
+    (*parg)->type = (*parg)->value->type;
+
+    return Check_Success;
+}
+
+static CheckStatus check_resolve_callee(AstCall* call, AstTyped** effective_callee) {
+    if (call->kind == Ast_Kind_Intrinsic_Call) return Check_Success;
+
+    AstTyped* callee = (AstTyped *) strip_aliases((AstNode *) call->callee);
+    b32 calling_a_macro = 0;
+
+    if (callee->kind == Ast_Kind_Overloaded_Function) {
+        AstTyped* new_callee = find_matching_overload_by_arguments(
+            ((AstOverloadedFunction *) callee)->overloads,
+            &call->args);
+
+        if (new_callee == NULL) {
+            report_unable_to_match_overload(call, ((AstOverloadedFunction *) callee)->overloads);
+            return Check_Error;
+        }
+
+        if (new_callee == (AstTyped *) &node_that_signals_a_yield) {
+            YIELD(call->token->pos, "Waiting for overloaded function option to pass type-checking.");
+        }
+
+        callee = new_callee;
+    }
+
+    if (callee->kind == Ast_Kind_Macro) {
+        calling_a_macro = 1;
+        call->callee = callee;
+
+        AstTyped* new_callee = (AstTyped *) macro_resolve_header((AstMacro *) callee, &call->args, call->token, 1);
+        if (new_callee == NULL) return Check_Error;
+        if (new_callee == (AstTyped *) &node_that_signals_a_yield) {
+            YIELD(call->token->pos, "Waiting for macro header to pass type-checking.");
+        }
+
+        arguments_remove_baked(&call->args);
+        callee = new_callee;
+
+    } else while (callee->kind == Ast_Kind_Polymorphic_Proc) {
+        AstTyped* new_callee = (AstTyped *) polymorphic_proc_lookup((AstFunction *) callee, PPLM_By_Arguments, &call->args, call->token);
+        if (new_callee == NULL) return Check_Error;
+        if (new_callee == (AstTyped *) &node_that_signals_a_yield) {
+            YIELD(call->token->pos, "Waiting for polymorphic procedure header to pass type-checking.");
+        }
+
+        arguments_remove_baked(&call->args);
+        callee = new_callee;
+    }
+
+    // NOTE: Build callee's type
+    fill_in_type((AstTyped *) callee);
+    if (callee->type == NULL) {
+        YIELD(call->token->pos, "Trying to resolve function type for callee.");
+    }
+
+    if (!calling_a_macro) call->callee = callee;
+
+    if (callee->type->kind != Type_Kind_Function) {
+        ERROR_(call->token->pos,
+                "Attempting to call something that is not a function, '%b'.",
+                callee->token->text, callee->token->length);
+    }
+
+    *effective_callee = callee;
+    return Check_Success;
+}
+
+CheckStatus check_call(AstCall** pcall) {
+    // All the things that need to be done when checking a call node.
+    //      1. Ensure the callee is not a symbol
+    //      2. Check the callee expression (since it could be a variable or a field access, etc)
+    //      3. Check all arguments
+    //          * Cannot pass overloaded functions (ROBUSTNESS)
+    //      4. If callee is an overloaded function, use the argument types to determine which overload is used.
+    //      5. If callee is polymorphic, use the arguments type to generate a polymorphic function.
+    //      7. Fill in arguments
+    //      8. If callee is an intrinsic, turn call into an Intrinsic_Call node
+    //      9. Check types of formal and actual params against each other, handling varargs
+    AstCall* call = *pcall;
+
+    if (call->kind == Ast_Kind_Call) {
+        AstNode* callee = strip_aliases((AstNode *) call->callee);
+        if (callee->kind == Ast_Kind_Poly_Struct_Type) {
+            *pcall = (AstCall *) convert_call_to_polycall(call);
+            CHECK(expression, (AstTyped **) pcall);
+            return Check_Success;
+        }
+    }
+
+    if (call->flags & Ast_Flag_Has_Been_Checked) return Check_Success;
+
+    u32 current_checking_level_store = current_checking_level;
+    CHECK(expression, &call->callee);
+    CHECK(arguments, &call->args);
+    current_checking_level = current_checking_level_store;
+
+    AstFunction* callee=NULL;
+    CHECK(resolve_callee, call, (AstTyped **) &callee);
+
+    if (callee->kind == Ast_Kind_Function) {
+        if (callee->constraints.constraints != NULL && callee->constraints.constraints_met == 0) {
+            YIELD(call->token->pos, "Waiting for constraints to be checked on callee.");
+        }
+    }
+
+    i32 arg_count = get_argument_buffer_size(&callee->type->Function, &call->args);
+    arguments_ensure_length(&call->args, arg_count);
+
+    char* err_msg = NULL;
+    fill_in_arguments(&call->args, (AstNode *) callee, &err_msg, 0);
+    if (err_msg != NULL) ERROR(call->token->pos, err_msg);
+
+    bh_arr(AstArgument *) arg_arr = (bh_arr(AstArgument *)) call->args.values;
+    bh_arr_each(AstArgument *, arg, arg_arr) {
+        if (*arg != NULL) continue;
+
+        ERROR(call->token->pos, "Not all arguments were given a value.");
+    }
+
+    // HACK HACK HACK
+    // :CallSiteIsGross
+    bh_arr_each(AstArgument *, arg, arg_arr) {
+        AstTyped** arg_value = &(*arg)->value;
+
+        if ((*arg_value)->kind == Ast_Kind_Call_Site) {
+            AstCallSite* callsite = (AstCallSite *) ast_clone(context.ast_alloc, *arg_value);
+            callsite->callsite_token = call->token;
+
+            // HACK CLEANUP
+            OnyxToken* str_token = bh_alloc(context.ast_alloc, sizeof(OnyxToken));
+            str_token->text  = bh_strdup(global_heap_allocator, (char *) call->token->pos.filename);
+            str_token->length = strlen(call->token->pos.filename);
+            str_token->pos = call->token->pos;
+            str_token->type = Token_Type_Literal_String;
+
+            AstStrLit* filename = bh_alloc_item(context.ast_alloc, AstStrLit);
+            memset(filename, 0, sizeof(AstStrLit));
+            filename->kind  = Ast_Kind_StrLit;
+            filename->token = str_token;
+            filename->data_id = 0;
+
+            add_entities_for_node(NULL, (AstNode *) filename, NULL, NULL);
+            callsite->filename = filename;
+
+            callsite->line   = make_int_literal(context.ast_alloc, call->token->pos.line);
+            callsite->column = make_int_literal(context.ast_alloc, call->token->pos.column);
+
+            convert_numlit_to_type(callsite->line,   &basic_types[Basic_Kind_U32]);
+            convert_numlit_to_type(callsite->column, &basic_types[Basic_Kind_U32]);
+
+            *arg_value = (AstTyped *) callsite;
+        }
+    }
+
+    // NOTE: If we are calling an intrinsic function, translate the
+    // call into an intrinsic call node.
+    if (callee->kind == Ast_Kind_Function && callee->is_intrinsic) {
+        call->kind = Ast_Kind_Intrinsic_Call;
+        call->callee = NULL;
+
+        token_toggle_end(callee->intrinsic_name);
+        char* intr_name = callee->intrinsic_name->text;
+
+        i32 index;
+        if ((index = shgeti(intrinsic_table, intr_name)) == -1) {
+            onyx_report_error(callee->token->pos, Error_Critical, "Intrinsic not supported, '%s'.", intr_name);
+            token_toggle_end(callee->intrinsic_name);
+            return Check_Error;
+        }
+
+        call->intrinsic = intrinsic_table[index].value;
+
+        token_toggle_end(callee->intrinsic_name);
+    }
+
+    call->va_kind = VA_Kind_Not_VA;
+    call->type = callee->type->Function.return_type;
+    if (call->type == &type_auto_return && call->callee->kind != Ast_Kind_Macro) {
+        YIELD(call->token->pos, "Waiting for auto-return type to be solved.");
+    }
+
+    OnyxError error;
+    TypeMatch tm = check_arguments_against_type(&call->args, &callee->type->Function, &call->va_kind,
+                                                call->token, get_function_name(callee), &error);
+    if (tm == TYPE_MATCH_FAILED) {
+        onyx_submit_error(error);
+        return Check_Error;
+    }
+
+    if (tm == TYPE_MATCH_YIELD) YIELD(call->token->pos, "Waiting on argument type checking.");
+
+    call->flags   |= Ast_Flag_Has_Been_Checked;
+    callee->flags |= Ast_Flag_Function_Used;
+
+    if (call->kind == Ast_Kind_Call && call->callee->kind == Ast_Kind_Macro) {
+        expand_macro(pcall, callee);
+        return Check_Return_To_Symres;
+    }
+
+    return Check_Success;
+}
+
+static void report_bad_binaryop(AstBinaryOp* binop) {
+    onyx_report_error(binop->token->pos, Error_Critical, "Binary operator '%s' not understood for arguments of type '%s' and '%s'.",
+            binaryop_string[binop->operation],
+            node_get_type_name(binop->left),
+            node_get_type_name(binop->right));
+}
+
+static AstCall* binaryop_try_operator_overload(AstBinaryOp* binop, AstTyped* third_argument) {
+    if (bh_arr_length(operator_overloads[binop->operation]) == 0) return NULL;
+
+    if (binop->overload_args == NULL) {
+        binop->overload_args = bh_alloc_item(context.ast_alloc, Arguments);
+        bh_arr_new(context.ast_alloc, binop->overload_args->values, 3);
+        bh_arr_set_length(binop->overload_args->values, third_argument ? 3 : 2);
+
+        if (binop_is_assignment(binop->operation)) {
+            binop->overload_args->values[0] = (AstTyped *) make_address_of(context.ast_alloc, binop->left);
+
+            u32 current_all_checks_are_final = all_checks_are_final;
+            all_checks_are_final = 0;
+            u32 current_checking_level_store = current_checking_level;
+            CheckStatus cs = check_address_of((AstAddressOf **) &binop->overload_args->values[0]);
+            current_checking_level = current_checking_level_store;
+            all_checks_are_final   = current_all_checks_are_final;
+
+            if (cs == Check_Yield_Macro)      return (AstCall *) &node_that_signals_a_yield;
+            if (cs == Check_Error)            return NULL;
+
+            binop->overload_args->values[0] = (AstTyped *) make_argument(context.ast_alloc, binop->overload_args->values[0]);
+
+        } else {
+            binop->overload_args->values[0] = (AstTyped *) make_argument(context.ast_alloc, binop->left);
+        }
+
+
+        binop->overload_args->values[1] = (AstTyped *) make_argument(context.ast_alloc, binop->right);
+        if (third_argument != NULL) binop->overload_args->values[2] = (AstTyped *) make_argument(context.ast_alloc, third_argument);
+    }
+
+    AstTyped* overload = find_matching_overload_by_arguments(operator_overloads[binop->operation], binop->overload_args);
+    if (overload == NULL || overload == (AstTyped *) &node_that_signals_a_yield) return (AstCall *) overload;
+
+    AstCall* implicit_call = onyx_ast_node_new(context.ast_alloc, sizeof(AstCall), Ast_Kind_Call);
+    implicit_call->token = binop->token;
+    implicit_call->callee = overload;
+    implicit_call->va_kind = VA_Kind_Not_VA;
+
+    arguments_clone(&implicit_call->args, binop->overload_args);
+    return implicit_call;
+}
+
+
+CheckStatus check_binaryop_assignment(AstBinaryOp** pbinop) {
+    AstBinaryOp* binop = *pbinop;
+    if (current_checking_level == EXPRESSION_LEVEL)
+        ERROR(binop->token->pos, "Assignment not valid in expression.");
+
+    if (!is_lval((AstNode *) binop->left))
+        ERROR_(binop->left->token->pos,
+                "Cannot assign to '%b'.",
+                binop->left->token->text, binop->left->token->length);
+
+    if ((binop->left->flags & Ast_Flag_Const) != 0 && binop->left->type != NULL)
+        ERROR_(binop->token->pos,
+                "Cannot assign to constant '%b'.",
+                binop->left->token->text, binop->left->token->length);
+
+    if (binop->operation == Binary_Op_Assign) {
+        // NOTE: Raw assignment
+
+        // NOTE: This is the 'type inference' system. Very stupid, but very easy.
+        // If a left operand has an unknown type, fill it in with the type of
+        // the right hand side.
+        if (binop->left->type == NULL) {
+            if (binop->left->type_node != NULL && binop->left->entity && binop->left->entity->state <= Entity_State_Check_Types) {
+                YIELD(binop->token->pos, "Waiting for type to be constructed on left hand side.");
+            }
+
+            // NOTE: There is a subtlety here. You cannot use the result of `resolve_expression_type` directly,
+            // as in some cases (especially with macros and polyprocs), the result is not "correct". The result
+            // makes them appears as though they are runtime-known values, which they are not. Using the following
+            // pattern does prevent this issue.
+            resolve_expression_type(binop->right);
+
+            Type* right_type = get_expression_type(binop->right);
+            if (right_type == NULL) {
+                if (binop->right->entity == NULL || binop->right->entity->state > Entity_State_Check_Types) {
+                    ERROR(binop->token->pos, "Could not resolve type of right hand side to infer.");
+
+                } else {
+                    YIELD(binop->token->pos, "Trying to resolve try of right hand side.");
+                }
+            }
+
+            if (right_type->kind == Type_Kind_Compound) {
+                AstCompound* lhs = (AstCompound *) binop->left;
+                i32 expr_count = right_type->Compound.count;
+                if (lhs->kind != Ast_Kind_Compound || bh_arr_length(lhs->exprs) != expr_count) {
+                    ERROR_(binop->token->pos, "Expected left hand side to have %d expressions.", expr_count);
+                }
+
+                fori (i, 0, expr_count) lhs->exprs[i]->type = right_type->Compound.types[i];
+
+                lhs->type = type_build_compound_type(context.ast_alloc, lhs);
+
+            } else {
+                binop->left->type = right_type;
+            }
+        }
+
+    } else {
+        // NOTE: +=, -=, ...
+        // NOTE: At this point, it is assumed that operator overloads for +=, -=, etc have been tested.
+
+        BinaryOp operation = -1;
+        if      (binop->operation == Binary_Op_Assign_Add)      operation = Binary_Op_Add;
+        else if (binop->operation == Binary_Op_Assign_Minus)    operation = Binary_Op_Minus;
+        else if (binop->operation == Binary_Op_Assign_Multiply) operation = Binary_Op_Multiply;
+        else if (binop->operation == Binary_Op_Assign_Divide)   operation = Binary_Op_Divide;
+        else if (binop->operation == Binary_Op_Assign_Modulus)  operation = Binary_Op_Modulus;
+        else if (binop->operation == Binary_Op_Assign_And)      operation = Binary_Op_And;
+        else if (binop->operation == Binary_Op_Assign_Or)       operation = Binary_Op_Or;
+        else if (binop->operation == Binary_Op_Assign_Xor)      operation = Binary_Op_Xor;
+        else if (binop->operation == Binary_Op_Assign_Shl)      operation = Binary_Op_Shl;
+        else if (binop->operation == Binary_Op_Assign_Shr)      operation = Binary_Op_Shr;
+        else if (binop->operation == Binary_Op_Assign_Sar)      operation = Binary_Op_Sar;
+
+        AstBinaryOp* new_right = make_binary_op(context.ast_alloc, operation, binop->left, binop->right);
+        binop->right = (AstTyped *) new_right;
+        new_right->token = binop->token;
+        binop->operation = Binary_Op_Assign;
+
+        CHECK(binaryop, (AstBinaryOp **) &binop->right);
+    }
+
+    if (binop->right->type == NULL) {
+        if (binop->right->entity != NULL && binop->right->entity->state <= Entity_State_Check_Types) {
+            YIELD(binop->token->pos, "Trying to resolve type of right hand side.");
+        }
+    }
+
+    TYPE_CHECK(&binop->right, binop->left->type) {
+        ERROR_(binop->token->pos,
+                "Cannot assign value of type '%s' to a '%s'.",
+                node_get_type_name(binop->right),
+                node_get_type_name(binop->left));
+    }
+
+    binop->type = &basic_types[Basic_Kind_Void];
+
+    return Check_Success;
+}
+
+static b32 binary_op_is_allowed(BinaryOp operation, Type* type) {
+    static const u8 binop_allowed[Binary_Op_Count] = {
+        /* Add */             Basic_Flag_Numeric | Basic_Flag_Pointer,
+        /* Minus */           Basic_Flag_Numeric | Basic_Flag_Pointer,
+        /* Multiply */        Basic_Flag_Numeric,
+        /* Divide */          Basic_Flag_Numeric,
+        /* Modulus */         Basic_Flag_Integer,
+
+        /* Equal */           Basic_Flag_Equality,
+        /* Not_Equal */       Basic_Flag_Equality,
+        /* Less */            Basic_Flag_Ordered,
+        /* Less_Equal */      Basic_Flag_Ordered,
+        /* Greater */         Basic_Flag_Ordered,
+        /* Greater_Equal */   Basic_Flag_Ordered,
+
+        /* And */             Basic_Flag_Integer,
+        /* Or */              Basic_Flag_Integer,
+        /* Xor */             Basic_Flag_Integer,
+        /* Shl */             Basic_Flag_Integer,
+        /* Shr */             Basic_Flag_Integer,
+        /* Sar */             Basic_Flag_Integer,
+
+        /* Bool_And */        Basic_Flag_Boolean,
+        /* Bool_Or */         Basic_Flag_Boolean,
+
+        /* Assign_Start */    0,
+        /* Assign */          0,
+        /* Assign_Add */      0,
+        /* Assign_Minus */    0,
+        /* Assign_Multiply */ 0,
+        /* Assign_Divide */   0,
+        /* Assign_Modulus */  0,
+        /* Assign_And */      0,
+        /* Assign_Or */       0,
+        /* Assign_Xor */      0,
+        /* Assign_Shl */      0,
+        /* Assign_Shr */      0,
+        /* Assign_Sar */      0,
+        /* Assign_End */      0,
+
+        /* Pipe */            0,
+        /* Range */           0,
+    };
+
+    enum BasicFlag effective_flags = 0;
+    switch (type->kind) {
+        case Type_Kind_Basic:    effective_flags = type->Basic.flags;  break;
+        case Type_Kind_Pointer:  effective_flags = Basic_Flag_Pointer; break;
+        case Type_Kind_Enum:     effective_flags = Basic_Flag_Integer; break;
+        case Type_Kind_Function: effective_flags = Basic_Flag_Equality; break;
+    }
+
+    return (binop_allowed[operation] & effective_flags) != 0;
+}
+
+CheckStatus check_binaryop_compare(AstBinaryOp** pbinop) {
+    AstBinaryOp* binop = *pbinop;
+
+    // HACK: Since ^... to rawptr is a one way conversion, strip any pointers
+    // away so they can be compared as expected
+    Type* ltype = binop->left->type;
+    Type* rtype = binop->right->type;
+
+    if (ltype == NULL) YIELD(binop->token->pos, "Waiting for left-type to be known.");
+    if (rtype == NULL) YIELD(binop->token->pos, "Waiting for right-type to be known.");
+
+    if (ltype->kind == Type_Kind_Pointer) ltype = &basic_types[Basic_Kind_Rawptr];
+    if (rtype->kind == Type_Kind_Pointer) rtype = &basic_types[Basic_Kind_Rawptr];
+
+    if (!types_are_compatible(ltype, rtype)) {
+        b32 left_ac  = node_is_auto_cast((AstNode *) binop->left);
+        b32 right_ac = node_is_auto_cast((AstNode *) binop->right);
+        if (left_ac && right_ac) ERROR(binop->token->pos, "Cannot have auto cast on both sides of binary operator.");
+
+        TYPE_CHECK(&binop->left, rtype) {
+            TYPE_CHECK(&binop->right, ltype) {
+                ERROR_(binop->token->pos,
+                        "Cannot compare '%s' to '%s'.",
+                        type_get_name(binop->left->type),
+                        type_get_name(binop->right->type));
+            }
+        }
+    }
+
+    if (!binary_op_is_allowed(binop->operation, binop->left->type)) {
+        report_bad_binaryop(binop);
+        return Check_Error;
+    }
+
+    binop->type = &basic_types[Basic_Kind_Bool];
+    if (binop->flags & Ast_Flag_Comptime) {
+        // NOTE: Not a binary op
+        *pbinop = (AstBinaryOp *) ast_reduce(context.ast_alloc, (AstTyped *) binop);
+    }
+
+    return Check_Success;
+}
+
+CheckStatus check_binaryop_bool(AstBinaryOp** pbinop) {
+    AstBinaryOp* binop = *pbinop;
+
+    if (!type_is_bool(binop->left->type) || !type_is_bool(binop->right->type)) {
+        report_bad_binaryop(binop);
+        return Check_Error;
+    }
+
+    binop->type = &basic_types[Basic_Kind_Bool];
+
+    if (binop->flags & Ast_Flag_Comptime) {
+        // NOTE: Not a binary op
+        *pbinop = (AstBinaryOp *) ast_reduce(context.ast_alloc, (AstTyped *) binop);
+    }
+    return Check_Success;
+}
+
+CheckStatus check_binaryop(AstBinaryOp** pbinop) {
+    AstBinaryOp* binop = *pbinop;
+
+    if (binop->flags & Ast_Flag_Has_Been_Checked) return Check_Success;
+
+    if (binop->operation == Binary_Op_Assign && binop->left->kind == Ast_Kind_Subscript && bh_arr_length(operator_overloads[Binary_Op_Subscript_Equals]) > 0) {
+        AstSubscript* sub = (AstSubscript *) binop->left;
+
+        if (binop->potential_substitute == NULL) {
+            u32 current_checking_level_store = current_checking_level;
+            CHECK(expression, &sub->addr);
+            CHECK(expression, &sub->expr);
+            CHECK(expression, &binop->right);
+            current_checking_level = current_checking_level_store;
+
+            AstBinaryOp *op = onyx_ast_node_new(context.ast_alloc, sizeof(AstBinaryOp), Ast_Kind_Binary_Op);
+            op->token = binop->token;
+            op->operation = Binary_Op_Subscript_Equals;
+            op->left  = ((AstSubscript *) binop->left)->addr;
+            op->right = ((AstSubscript *) binop->left)->expr;
+
+            binop->potential_substitute = op;
+        }
+
+        AstCall* call = binaryop_try_operator_overload(binop->potential_substitute, binop->right);
+        if (call == (AstCall *) &node_that_signals_a_yield) YIELD(binop->token->pos, "Waiting on potential operator overload.");
+        if (call != NULL) {
+            call->next = binop->next;
+            *(AstCall **) pbinop = call;
+
+            CHECK(call, (AstCall **) pbinop);
+            return Check_Success;
+        }
+
+    }
+
+    u32 current_checking_level_store = current_checking_level;
+    CHECK(expression, &binop->left);
+    CHECK(expression, &binop->right);
+    current_checking_level = current_checking_level_store;
+
+    // :UnaryFieldAccessIsGross
+    if (binop->left->kind == Ast_Kind_Unary_Field_Access || binop->right->kind == Ast_Kind_Unary_Field_Access) {
+        TYPE_CHECK(&binop->left, binop->right->type) {
+            TYPE_CHECK(&binop->right, binop->left->type) {
+                // TODO: This should report a better error about the Unary_Field_Access not be able to be resolved given whatever type.
+                //                                                                        - brendanfh 2021/12/31
+                report_bad_binaryop(binop);
+                return Check_Error;
+            }
+        }
+    }
+
+    if ((binop->left->flags & Ast_Flag_Comptime) && (binop->right->flags & Ast_Flag_Comptime)) {
+        binop->flags |= Ast_Flag_Comptime;
+    }
+
+    if (expression_types_must_be_known) {
+        if (binop->left->type == NULL || binop->right->type == NULL) {
+            ERROR(binop->token->pos, "Internal compiler error: one of the operands types is unknown here.");
+        }
+    }
+
+    // NOTE: Try operator overloading before checking everything else.
+    if ((binop->left->type != NULL && (binop->left->type->kind != Type_Kind_Basic || (binop->left->type->Basic.flags & Basic_Flag_SIMD) != 0))
+        || (binop->right->type != NULL && (binop->right->type->kind != Type_Kind_Basic || (binop->right->type->Basic.flags & Basic_Flag_SIMD) != 0))) {
+        AstCall *implicit_call = binaryop_try_operator_overload(binop, NULL);
+
+        if (implicit_call == (AstCall *) &node_that_signals_a_yield)
+            YIELD(binop->token->pos, "Trying to resolve operator overload.");
+
+        if (implicit_call != NULL) {
+            // NOTE: Not a binary op
+            implicit_call->next = binop->next;
+            *pbinop = (AstBinaryOp *) implicit_call;
+
+            CHECK(call, (AstCall **) pbinop);
+            return Check_Success;
+        }
+    }
+
+    if (binop_is_assignment(binop->operation)) return check_binaryop_assignment(pbinop);
+
+    if (binop->left->type == NULL && binop->left->entity && binop->left->entity->state <= Entity_State_Check_Types) {
+        YIELD(binop->left->token->pos, "Waiting for this type to be known");
+    }
+    if (binop->right->type == NULL && binop->right->entity && binop->right->entity->state <= Entity_State_Check_Types) {
+        YIELD(binop->right->token->pos, "Waiting for this type to be known");
+    }
+
+    // NOTE: Comparision operators and boolean operators are handled separately.
+    if (binop_is_compare(binop->operation))
+        return check_binaryop_compare(pbinop);
+    if (binop->operation == Binary_Op_Bool_And || binop->operation == Binary_Op_Bool_Or)
+        return check_binaryop_bool(pbinop);
+
+    // NOTE: The left side cannot be compound.
+    //       The right side always is numeric.
+    //       The left side cannot be rawptr.
+    if (type_is_compound(binop->left->type))  goto bad_binaryop;
+    if (!type_is_numeric(binop->right->type)) goto bad_binaryop;
+    if (type_is_rawptr(binop->left->type)) {
+        ERROR(binop->token->pos, "Cannot operate on a 'rawptr'. Cast it to a another pointer type first.");
+    }
+
+    // NOTE: Handle basic pointer math.
+    if (type_is_pointer(binop->left->type)) {
+        if (binop->operation != Binary_Op_Add && binop->operation != Binary_Op_Minus) goto bad_binaryop;
+
+        resolve_expression_type(binop->right);
+        if (!type_is_integer(binop->right->type)) goto bad_binaryop;
+
+        AstNumLit* numlit = make_int_literal(context.ast_alloc, type_size_of(binop->left->type->Pointer.elem));
+        numlit->token = binop->right->token;
+        numlit->type = binop->right->type;
+
+        AstBinaryOp* binop_node = make_binary_op(context.ast_alloc, Binary_Op_Multiply, binop->right, (AstTyped *) numlit);
+        binop_node->token = binop->token;
+        CHECK(binaryop, &binop_node);
+
+        binop->right = (AstTyped *) binop_node;
+        binop->type = binop->left->type;
+        binop->right->type = binop->left->type;
+    }
+
+    if (!types_are_compatible(binop->left->type, binop->right->type)) {
+        b32 left_ac  = node_is_auto_cast((AstNode *) binop->left);
+        b32 right_ac = node_is_auto_cast((AstNode *) binop->right);
+        if (left_ac && right_ac) {
+            ERROR(binop->token->pos, "Cannot have auto cast on both sides of binary operator.");
+        }
+
+        TYPE_CHECK(&binop->left, binop->right->type) {
+            TYPE_CHECK(&binop->right, binop->left->type) {
+                ERROR_(binop->token->pos,
+                        "Mismatched types for binary operation '%s'. left: '%s', right: '%s'.",
+                        binaryop_string[binop->operation],
+                        node_get_type_name(binop->left),
+                        node_get_type_name(binop->right));
+            }
+        }
+    }
+
+    binop->type = binop->left->type;
+    if (!binary_op_is_allowed(binop->operation, binop->type)) goto bad_binaryop;
+
+    // NOTE: Enum flags with '&' result in a boolean value
+    if (binop->type->kind == Type_Kind_Enum && binop->type->Enum.is_flags && binop->operation == Binary_Op_And) {
+         binop->type = &basic_types[Basic_Kind_Bool];
+    }
+
+    if (all_checks_are_final) {
+        binop->flags |= Ast_Flag_Has_Been_Checked;
+
+        if (binop->flags & Ast_Flag_Comptime) {
+            // NOTE: Not a binary op
+            *pbinop = (AstBinaryOp *) ast_reduce(context.ast_alloc, (AstTyped *) binop);
+        }
+    }
+
+    return Check_Success;
+
+bad_binaryop:
+    report_bad_binaryop(binop);
+
+    return Check_Error;
+}
+
+CheckStatus check_unaryop(AstUnaryOp** punop) {
+    AstUnaryOp* unaryop = *punop;
+
+    CHECK(expression, &unaryop->expr);
+
+    if (unaryop->operation != Unary_Op_Negate) {
+        resolve_expression_type(unaryop->expr);
+    }
+
+    if (unaryop->operation == Unary_Op_Cast) {
+        char* err;
+        if (unaryop->type == NULL)
+            YIELD(unaryop->token->pos, "Trying to resolve destination type for cast.");
+
+        if (!cast_is_legal(unaryop->expr->type, unaryop->type, &err)) {
+            ERROR_(unaryop->token->pos, "Cast Error: %s", err);
+        }
+
+    } else {
+        unaryop->type = unaryop->expr->type;
+    }
+
+    if (unaryop->operation == Unary_Op_Not) {
+        if (!type_is_bool(unaryop->expr->type)) {
+            ERROR_(unaryop->token->pos,
+                    "Bool negation operator expected bool type, got '%s'.",
+                    node_get_type_name(unaryop->expr));
+        }
+    }
+
+    if (unaryop->operation == Unary_Op_Bitwise_Not) {
+        if (!type_is_integer(unaryop->expr->type)) {
+            ERROR_(unaryop->token->pos,
+                    "Bitwise operator expected integer type, got '%s'.",
+                    node_get_type_name(unaryop->expr));
+        }
+    }
+
+    if (unaryop->expr->flags & Ast_Flag_Comptime) {
+        unaryop->flags |= Ast_Flag_Comptime;
+        // NOTE: Not a unary op
+        *punop = (AstUnaryOp *) ast_reduce(context.ast_alloc, (AstTyped *) unaryop);
+    }
+
+    return Check_Success;
+}
+
+CheckStatus check_struct_literal(AstStructLiteral* sl) {
+
+    if (sl->type == NULL) {
+        // NOTE: This is used for automatically typed struct literals. If there is no provided
+        // type for the struct literal, assume that it is passes successfully. When it is used
+        // elsewhere, it will be added as an expression entity that will be processed once the
+        // stnode is filled out.
+        if (sl->stnode == NULL) {
+            CHECK(arguments, &sl->args);
+
+            return Check_Success;
+        }
+
+        CHECK(expression, &sl->stnode);
+        if (!node_is_type((AstNode *) sl->stnode)) {
+            ERROR(sl->token->pos, "Type used for struct literal is not a type.");
+        }
+
+        sl->type = type_build_from_ast(context.ast_alloc, (AstType *) sl->stnode);
+        if (sl->type == NULL)
+            YIELD(sl->token->pos, "Trying to resolve type of struct literal.");
+    }
+
+    if (!type_is_structlike_strict(sl->type)) {
+        //
+        // If there are no given arguments to a structure literal, it is treated as a 'zero-value',
+        // and can be used to create a completely zeroed value of any type.
+        if (bh_arr_length(sl->args.values) == 0 && bh_arr_length(sl->args.named_values) == 0) {
+            AstZeroValue *zv = make_zero_value(context.ast_alloc, sl->token, sl->type);
+            bh_arr_push(sl->args.values, (AstTyped *) zv);
+
+            sl->flags |= Ast_Flag_Has_Been_Checked;
+            return Check_Success;
+        }
+
+        if ((sl->flags & Ast_Flag_Has_Been_Checked) != 0) {
+            assert(sl->args.values);
+            assert(sl->args.values[0]);
+            assert(sl->args.values[0]->kind == Ast_Kind_Zero_Value);
+            return Check_Success;
+        }
+
+        //
+        // Otherwise, it is not possible to construct the type if it is not a structure.
+        ERROR_(sl->token->pos,
+                "'%s' is not constructable using a struct literal.",
+                type_get_name(sl->type));
+    }
+
+    i32 mem_count = type_structlike_mem_count(sl->type);
+    arguments_ensure_length(&sl->args, mem_count);
+
+    // :Idempotency
+    if ((sl->flags & Ast_Flag_Has_Been_Checked) == 0) {
+        char* err_msg = NULL;
+        if (!fill_in_arguments(&sl->args, (AstNode *) sl, &err_msg, 1)) {
+            onyx_report_error(sl->token->pos, Error_Critical, err_msg);
+
+            bh_arr_each(AstTyped *, value, sl->args.values) {
+                if (*value == NULL) {
+                    i32 member_idx = value - sl->args.values; // Pointer subtraction hack
+                    StructMember smem;
+                    type_lookup_member_by_idx(sl->type, member_idx, &smem);
+
+                    onyx_report_error(sl->token->pos, Error_Critical,
+                        "Value not given for %d%s member, '%s', for type '%s'.",
+                        member_idx + 1, bh_num_suffix(member_idx + 1),
+                        smem.name, type_get_name(sl->type));
+                }
+            }
+
+            return Check_Error;
+        }
+    }
+    sl->flags |= Ast_Flag_Has_Been_Checked;
+
+    AstTyped** actual = sl->args.values;
+    StructMember smem;
+
+    // BUG: There are problems setting the comptime flag this late in the checking because
+    // if the struct literal was type inferred, then the literal won't be correctly determined
+    // to be comptime on the first pass, which is needed for top level expressions.
+    sl->flags |= Ast_Flag_Comptime;
+
+    fori (i, 0, mem_count) {
+        // NOTE: Not checking the return on this function because
+        // this for loop is bounded by the number of members in the
+        // type.
+        type_lookup_member_by_idx(sl->type, i, &smem);
+        Type* formal = smem.type;
+
+        CHECK(expression, actual);
+        if ((*actual)->type == NULL && (*actual)->entity != NULL && (*actual)->entity->state <= Entity_State_Check_Types) {
+            YIELD((*actual)->token->pos, "Trying to resolve type of expression for member.");
+        }
+
+        TYPE_CHECK(actual, formal) {
+            ERROR_(sl->token->pos,
+                    "Mismatched types for %d%s member named '%s', expected '%s', got '%s'.",
+                    i + 1, bh_num_suffix(i + 1),
+                    smem.name,
+                    type_get_name(formal),
+                    node_get_type_name(*actual));
+        }
+
+        sl->flags &= ((*actual)->flags & Ast_Flag_Comptime) | (sl->flags &~ Ast_Flag_Comptime);
+        actual++;
+    }
+
+    return Check_Success;
+}
+
+CheckStatus check_array_literal(AstArrayLiteral* al) {
+    // :Idempotency
+    if ((al->flags & Ast_Flag_Array_Literal_Typed) == 0) {
+        if (al->atnode == NULL) return Check_Success;
+            // YIELD(al->token->pos, "Waiting for array literal type to be known.");
+
+        CHECK(expression, &al->atnode);
+        if (!node_is_type((AstNode *) al->atnode))
+            ERROR(al->token->pos, "Array type is not a type.");
+
+        al->type = type_build_from_ast(context.ast_alloc, (AstType *) al->atnode);
+        if (al->type == NULL)
+            YIELD(al->token->pos, "Trying to resolve type of array literal.");
+
+        al->type = type_make_array(context.ast_alloc, al->type, bh_arr_length(al->values));
+        if (al->type == NULL || al->type->kind != Type_Kind_Array)
+            ERROR(al->token->pos, "Expected array type for array literal. This is a compiler bug.");
+
+        al->flags |= Ast_Flag_Array_Literal_Typed;
+    }
+
+    if (al->type->Array.count != (u32) bh_arr_length(al->values)) {
+        ERROR_(al->token->pos, "Wrong array size (%d) for number of values (%d).",
+            al->type->Array.count, bh_arr_length(al->values));
+    }
+
+    al->flags |= Ast_Flag_Comptime;
+    assert(al->type->kind == Type_Kind_Array);
+
+    Type* elem_type = al->type->Array.elem;
+    bh_arr_each(AstTyped *, expr, al->values) {
+        CHECK(expression, expr);
+
+        // HACK HACK HACK
+        if ((*expr)->type == NULL &&
+            (*expr)->entity != NULL &&
+            (*expr)->entity->state <= Entity_State_Check_Types) {
+            YIELD_(al->token->pos, "Trying to resolve type of %d%s element of array literal.", expr - al->values, bh_num_suffix(expr - al->values));
+        }
+
+        al->flags &= ((*expr)->flags & Ast_Flag_Comptime) | (al->flags &~ Ast_Flag_Comptime);
+
+        TYPE_CHECK(expr, elem_type) {
+            ERROR_((*expr)->token->pos, "Mismatched types for value of in array, expected '%s', got '%s'.",
+                type_get_name(elem_type),
+                node_get_type_name(*expr));
+        }
+    }
+
+    return Check_Success;
+}
+
+CheckStatus check_range_literal(AstRangeLiteral** prange) {
+    AstRangeLiteral* range = *prange;
+    if (range->flags & Ast_Flag_Has_Been_Checked) return Check_Success;
+
+    CHECK(expression, &range->low);
+    CHECK(expression, &range->high);
+
+    builtin_range_type_type = type_build_from_ast(context.ast_alloc, builtin_range_type);
+    if (builtin_range_type_type == NULL) YIELD(range->token->pos, "Waiting for 'range' structure to be built.");
+
+    Type* expected_range_type = builtin_range_type_type;
+    StructMember smem;
+
+    type_lookup_member(expected_range_type, "low", &smem);
+    TYPE_CHECK(&range->low, smem.type) {
+        ERROR_(range->token->pos,
+            "Expected left side of range to be a 32-bit integer, got '%s'.",
+            node_get_type_name(range->low));
+    }
+
+    type_lookup_member(expected_range_type, "high", &smem);
+    TYPE_CHECK(&range->high, smem.type) {
+        ERROR_(range->token->pos,
+            "Expected right side of range to be a 32-bit integer, got '%s'.",
+            node_get_type_name(range->high));
+    }
+
+    if (range->step == NULL) {
+        type_lookup_member(expected_range_type, "step", &smem);
+        assert(smem.initial_value != NULL);
+        CHECK(expression, smem.initial_value);
+
+        range->step = *smem.initial_value;
+    }
+
+    range->flags |= Ast_Flag_Has_Been_Checked;
+    return Check_Success;
+}
+
+CheckStatus check_compound(AstCompound* compound) {
+    bh_arr_each(AstTyped *, expr, compound->exprs) {
+        CHECK(expression, expr);
+    }
+
+    compound->type = type_build_compound_type(context.ast_alloc, compound);
+    return Check_Success;
+}
+
+CheckStatus check_if_expression(AstIfExpression* if_expr) {
+    CHECK(expression, &if_expr->cond);
+    CHECK(expression, &if_expr->true_expr);
+    CHECK(expression, &if_expr->false_expr);
+
+    TYPE_CHECK(&if_expr->cond, &basic_types[Basic_Kind_Bool]) {
+        ERROR_(if_expr->token->pos, "If-expression expected boolean for condition, got '%s'.",
+            type_get_name(if_expr->cond->type));
+    }
+
+    resolve_expression_type((AstTyped *) if_expr);
+
+    if (!types_are_compatible(if_expr->true_expr->type, if_expr->false_expr->type)) {
+        ERROR_(if_expr->token->pos, "Mismatched types for if-expression, left side is '%s', and right side is '%s'.",
+            type_get_name(if_expr->true_expr->type), type_get_name(if_expr->false_expr->type));
+    }
+
+    return Check_Success;
+}
+
+CheckStatus check_do_block(AstDoBlock** pdoblock) {
+    AstDoBlock* doblock = *pdoblock;
+    if (doblock->flags & Ast_Flag_Has_Been_Checked) return Check_Success;
+
+    fill_in_type((AstTyped *) doblock);
+
+    Type** old_expected_return_type = expected_return_type;
+    expected_return_type = &doblock->type;
+
+    doblock->block->rules = Block_Rule_Do_Block;
+    CHECK(block, doblock->block);
+
+    if (doblock->type == &type_auto_return) doblock->type = &basic_types[Basic_Kind_Void];
+
+    expected_return_type = old_expected_return_type;
+    doblock->flags |= Ast_Flag_Has_Been_Checked;
+    return Check_Success;
+}
+
+CheckStatus check_address_of(AstAddressOf** paof) {
+    AstAddressOf* aof = *paof;
+
+    AstTyped* expr = (AstTyped *) strip_aliases((AstNode *) aof->expr);
+    if (expr->kind == Ast_Kind_Subscript && bh_arr_length(operator_overloads[Binary_Op_Ptr_Subscript]) > 0) {
+        if (aof->potential_substitute == NULL) {
+            CHECK(expression, &((AstSubscript *) expr)->addr);
+            CHECK(expression, &((AstSubscript *) expr)->expr);
+
+            AstBinaryOp *op = onyx_ast_node_new(context.ast_alloc, sizeof(AstBinaryOp), Ast_Kind_Binary_Op);
+            op->operation = Binary_Op_Ptr_Subscript;
+            op->left  = ((AstSubscript *) expr)->addr;
+            op->right = ((AstSubscript *) expr)->expr;
+            op->token = aof->token;
+
+            aof->potential_substitute = op;
+        }
+
+        AstCall* call = binaryop_try_operator_overload(aof->potential_substitute, NULL);
+        if (call == (AstCall *) &node_that_signals_a_yield) YIELD(aof->token->pos, "Waiting for operator overload to possibly resolve.");
+        if (call != NULL) {
+            call->next = aof->next;
+            *(AstCall **) paof = call;
+
+            CHECK(call, (AstCall **) paof);
+            return Check_Success;
+        }
+    }
+
+    CHECK(expression, &aof->expr);
+    if (node_is_addressable_literal((AstNode *) aof->expr)) {
+        resolve_expression_type(aof->expr);
+    }
+    
+    if (aof->expr->type == NULL) {
+        YIELD(aof->token->pos, "Trying to resolve type of expression to take a reference.");
+    }
+
+    expr = (AstTyped *) strip_aliases((AstNode *) aof->expr);
+    if (node_is_type((AstNode *) expr)) {
+        AstPointerType *pt = onyx_ast_node_new(context.ast_alloc, sizeof(AstPointerType), Ast_Kind_Pointer_Type);
+        pt->token     = aof->token;
+        pt->elem      = (AstType *) expr;
+        pt->__unused  = aof->next;
+        *paof         = (AstAddressOf *) pt;
+        CHECK(type, (AstType **) &pt);
+        return Check_Success;
+    }
+
+    if ((expr->kind != Ast_Kind_Subscript
+            && expr->kind != Ast_Kind_Dereference
+            && expr->kind != Ast_Kind_Field_Access
+            && expr->kind != Ast_Kind_Memres
+            && expr->kind != Ast_Kind_Local
+            && expr->kind != Ast_Kind_Constraint_Sentinel
+            && !node_is_addressable_literal((AstNode *) expr))
+            || (expr->flags & Ast_Flag_Cannot_Take_Addr) != 0) {
+
+        if (aof->can_be_removed) {
+            *(AstTyped **) paof = aof->expr;
+            return Check_Yield_Macro;
+        }
+
+        ERROR_(aof->token->pos, "Cannot take the address of something that is not an l-value. %s", onyx_ast_node_kind_string(expr->kind));
+    }
+
+    expr->flags |= Ast_Flag_Address_Taken;
+
+    aof->type = type_make_pointer(context.ast_alloc, expr->type);
+
+    return Check_Success;
+}
+
+CheckStatus check_dereference(AstDereference* deref) {
+    CHECK(expression, &deref->expr);
+
+    if (!type_is_pointer(deref->expr->type))
+        ERROR(deref->token->pos, "Cannot dereference non-pointer value.");
+
+    if (deref->expr->type == basic_type_rawptr.basic_type)
+        ERROR(deref->token->pos, "Cannot dereference 'rawptr'. Cast to another pointer type first.");
+
+    deref->type = deref->expr->type->Pointer.elem;
+
+    return Check_Success;
+}
+
+CheckStatus check_subscript(AstSubscript** psub) {
+    AstSubscript* sub = *psub;
+    CHECK(expression, &sub->addr);
+    CHECK(expression, &sub->expr);
+
+    if (sub->addr->type == NULL) YIELD(sub->token->pos, "Waiting to know type of left-hand side of subscript.");
+
+    // NOTE: Try operator overloading before checking everything else.
+    if (sub->expr->type != NULL &&
+        (sub->addr->type->kind != Type_Kind_Basic || sub->expr->type->kind != Type_Kind_Basic)) {
+        // AstSubscript is the same as AstBinaryOp for the first sizeof(AstBinaryOp) bytes
+        AstBinaryOp* binop = (AstBinaryOp *) sub;
+        AstCall *implicit_call = binaryop_try_operator_overload(binop, NULL);
+
+        if (implicit_call == (AstCall *) &node_that_signals_a_yield)
+            YIELD(sub->token->pos, "Trying to resolve operator overload.");
+
+        if (implicit_call != NULL) {
+            // NOTE: Not an array access
+            implicit_call->next = sub->next;
+            *psub = (AstSubscript *) implicit_call;
+
+            CHECK(call, (AstCall **) psub);
+            return Check_Success;
+        }
+    }
+
+    if (!type_is_array_accessible(sub->addr->type)) {
+        report_bad_binaryop((AstBinaryOp *) sub);
+        return Check_Error;
+    }
+
+    if (sub->addr->type->kind == Type_Kind_Slice || sub->addr->type->kind == Type_Kind_DynArray || sub->addr->type->kind == Type_Kind_VarArgs) {
+        // If we are accessing on a slice or a dynamic array, implicitly add a field access for the data member
+        StructMember smem;
+        type_lookup_member(sub->addr->type, "data", &smem);
+
+        AstFieldAccess* fa = make_field_access(context.ast_alloc, sub->addr, "data");
+        fa->type   = smem.type;
+        fa->offset = smem.offset;
+        fa->idx    = smem.idx;
+
+        sub->addr = (AstTyped *) fa;
+    }
+
+    if (types_are_compatible(sub->expr->type, builtin_range_type_type)) {
+        Type *of = type_get_contained_type(sub->addr->type);
+        if (of == NULL) {
+            // FIXME: Slice creation should be allowed for slice types and dynamic array types, like it
+            // is below, but this code doesn't look at that.
+            report_bad_binaryop((AstBinaryOp *) sub);
+            ERROR(sub->token->pos, "Invalid type for left of slice creation.");
+        }
+
+        sub->kind = Ast_Kind_Slice;
+        sub->type = type_make_slice(context.ast_alloc, of);
+        sub->elem_size = type_size_of(of);
+
+        return Check_Success;
+    }
+
+    resolve_expression_type(sub->expr);
+    if (!type_is_small_integer(sub->expr->type)) {
+        report_bad_binaryop((AstBinaryOp *) sub);
+        ERROR_(sub->token->pos, "Expected small integer type for index, got '%s'.", node_get_type_name(sub->expr));
+    }
+
+    sub->type = type_get_contained_type(sub->addr->type);
+    if (sub->type == NULL) {
+        report_bad_binaryop((AstBinaryOp *) sub);
+        ERROR(sub->token->pos, "Invalid type for left of array access.");
+    }
+
+    sub->elem_size = type_size_of(sub->type);
+    return Check_Success;
+}
+
+CheckStatus check_field_access(AstFieldAccess** pfield) {
+    AstFieldAccess* field = *pfield;
+    if (field->flags & Ast_Flag_Has_Been_Checked) return Check_Success;
+
+    CHECK(expression, &field->expr);
+    if (field->expr->type == NULL) {
+        YIELD(field->token->pos, "Trying to resolve type of source expression.");
+    }
+
+    if (!type_is_structlike(field->expr->type)) {
+        ERROR_(field->token->pos,
+            "Cannot access field '%b' on '%s'. Type is not a struct.",
+            field->token->text,
+            field->token->length,
+            node_get_type_name(field->expr));
+    }
+
+    // Optimization for (*foo).member.
+    if (field->expr->kind == Ast_Kind_Dereference) {
+        field->expr = ((AstDereference *) field->expr)->expr;
+    }
+
+    if (field->token != NULL && field->field == NULL) {
+        token_toggle_end(field->token);
+        field->field = bh_strdup(context.ast_alloc, field->token->text);
+        token_toggle_end(field->token);
+    }
+
+    if (field->expr->type->kind == Type_Kind_Struct) {
+        if (field->expr->type->Struct.status != SPS_Uses_Done) {
+            YIELD(field->token->pos, "Waiting for struct type to be completed before looking up members.");
+        }
+    }
+
+    StructMember smem;
+    if (!type_lookup_member(field->expr->type, field->field, &smem)) {
+        if (field->expr->type->kind == Type_Kind_Array) {
+            if (!strcmp(field->field, "count")) {
+                *pfield = (AstFieldAccess *) make_int_literal(context.ast_alloc, field->expr->type->Array.count);
+                return Check_Success;
+            }
+        }
+
+        AstNode* n = try_symbol_raw_resolve_from_type(field->expr->type, field->field);
+
+        AstType* type_node = field->expr->type->ast_type;
+        if (!n) n = try_symbol_raw_resolve_from_node((AstNode *) type_node, field->field);
+
+        if (n) {
+            *pfield = (AstFieldAccess *) n;
+            return Check_Success;
+        }
+
+        char* closest = find_closest_symbol_in_node((AstNode *) type_node, field->field);
+        if (closest) {
+            ERROR_(field->token->pos, "Field '%s' does not exists on '%s'. Did you mean '%s'?", field->field, node_get_type_name(field->expr), closest);
+        } else {
+            ERROR_(field->token->pos, "Field '%s' does not exists on '%s'.", field->field, node_get_type_name(field->expr));
+        }
+    }
+
+    // NOTE: If this member was included into the structure through a "use x: ^T" kind of statement,
+    // then we have to insert a intermediate field access in order to access the correct member.
+    if (smem.use_through_pointer_index >= 0) {
+        StructMember containing_member;
+        assert(type_lookup_member_by_idx(field->expr->type, smem.use_through_pointer_index, &containing_member));
+
+        AstFieldAccess *new_access = onyx_ast_node_new(context.ast_alloc, sizeof(AstFieldAccess), Ast_Kind_Field_Access);
+        new_access->token = field->token;
+        new_access->offset = containing_member.offset;
+        new_access->idx = containing_member.idx;
+        new_access->type = containing_member.type;
+        new_access->expr = field->expr;
+        new_access->flags |= Ast_Flag_Has_Been_Checked;
+
+        field->expr = (AstTyped *) new_access;
+    }
+
+    field->offset = smem.offset;
+    field->idx = smem.idx;
+    field->type = smem.type;
+    field->flags |= Ast_Flag_Has_Been_Checked;
+    return Check_Success;
+}
+
+CheckStatus check_method_call(AstBinaryOp** pmcall) {
+    AstBinaryOp* mcall = *pmcall;
+    CHECK(expression, &mcall->left);
+    if (mcall->left->type == NULL) YIELD(mcall->token->pos, "Trying to resolve type of left hand side.");
+
+    mcall->type = mcall->left->type;
+    AstTyped* implicit_argument = mcall->left;
+
+    // Symbol resolution should have ensured that this is call node.
+    AstCall* call_node = (AstCall *) mcall->right;
+    assert(call_node->kind == Ast_Kind_Call);
+
+    // :Idempotency
+    if ((mcall->flags & Ast_Flag_Has_Been_Checked) == 0) {
+        // Implicitly take the address of the value if it is not already a pointer type.
+        // This could be weird to think about semantically so some testing with real code
+        // would be good.                                      - brendanfh 2020/02/05
+        if (implicit_argument->type->kind != Type_Kind_Pointer) {
+            AstAddressOf *address_of = make_address_of(context.ast_alloc, implicit_argument);
+            address_of->can_be_removed = 1;
+            implicit_argument = (AstTyped *) address_of;
+        }
+
+        implicit_argument = (AstTyped *) make_argument(context.ast_alloc, implicit_argument);
+
+        bh_arr_insertn(call_node->args.values, 0, 1);
+        call_node->args.values[0] = implicit_argument;
+
+        *pmcall = (AstBinaryOp *) mcall->right;
+        mcall->right->next = mcall->next;
+    }
+    mcall->flags |= Ast_Flag_Has_Been_Checked;
+
+    CHECK(call, (AstCall **) pmcall);
+    return Check_Success;
+}
+
+CheckStatus check_size_of(AstSizeOf* so) {
+    CHECK(type, &so->so_ast_type);
+
+    so->so_type = type_build_from_ast(context.ast_alloc, so->so_ast_type);
+    if (so->so_type == NULL)
+        YIELD(so->token->pos, "Trying to resolve type to take the size of.");
+
+    so->size = type_size_of(so->so_type);
+    so->flags |= Ast_Flag_Comptime;
+
+    return Check_Success;
+}
+
+CheckStatus check_align_of(AstAlignOf* ao) {
+    CHECK(type, &ao->ao_ast_type);
+
+    ao->ao_type = type_build_from_ast(context.ast_alloc, ao->ao_ast_type);
+    if (ao->ao_type == NULL)
+        YIELD(ao->token->pos, "Trying to resolve type to take the alignment of.");
+
+    ao->alignment = type_alignment_of(ao->ao_type);
+    ao->flags |= Ast_Flag_Comptime;
+
+    return Check_Success;
+}
+
+CheckStatus check_expression(AstTyped** pexpr) {
+    AstTyped* expr = *pexpr;
+    if (expr->kind > Ast_Kind_Type_Start && expr->kind < Ast_Kind_Type_End) {
+        // This is to ensure that the type will exist when compiling. For example, a poly-call type
+        // would have to wait for the entity to pass through, which the code generation does not know
+        // about.
+        CHECK(type, (AstType **) pexpr);
+        expr = *pexpr;
+
+        // Don't try to construct a polystruct ahead of time because you can't.
+        if (expr->kind != Ast_Kind_Poly_Struct_Type) {
+            if (type_build_from_ast(context.ast_alloc, (AstType*) expr) == NULL) {
+                YIELD(expr->token->pos, "Trying to construct type.");
+            }
+        } else {
+            type_build_from_ast(context.ast_alloc, (AstType*) expr);
+        }
+
+        expr->type = &basic_types[Basic_Kind_Type_Index];
+        return Check_Success;
+    }
+
+    if (expr->kind == Ast_Kind_Polymorphic_Proc) {
+        // polymorphic procedures do not need to be checked. Their concrete instantiations
+        // will be checked when they are created.
+        return Check_Success;
+    }
+
+    if (expr->kind == Ast_Kind_Macro) {
+        return Check_Success;
+    }
+
+    if (expr->kind == Ast_Kind_Directive_Init) {
+        ERROR(expr->token->pos, "#init declarations are not in normal expressions, only in #after clauses.");
+    }
+
+    fill_in_type(expr);
+    current_checking_level = EXPRESSION_LEVEL;
+
+    CheckStatus retval = Check_Success;
+    switch (expr->kind) {
+        case Ast_Kind_Binary_Op: retval = check_binaryop((AstBinaryOp **) pexpr); break;
+        case Ast_Kind_Unary_Op:  retval = check_unaryop((AstUnaryOp **) pexpr); break;
+
+        case Ast_Kind_Intrinsic_Call:
+        case Ast_Kind_Call:     retval = check_call((AstCall **) pexpr); break;
+        case Ast_Kind_Argument: retval = check_argument((AstArgument **) pexpr); break;
+        case Ast_Kind_Block:    retval = check_block((AstBlock *) expr); break;
+
+        case Ast_Kind_Symbol:
+            YIELD_(expr->token->pos, "Waiting to resolve symbol, '%b'.", expr->token->text, expr->token->length);
+            break;
+
+        case Ast_Kind_Param:
+            if (expr->type == NULL) {
+                YIELD(expr->token->pos, "Waiting on parameter type.");
+            }
+            break;
+
+        case Ast_Kind_Local: break;
+
+        case Ast_Kind_Address_Of:    retval = check_address_of((AstAddressOf **) pexpr); break;
+        case Ast_Kind_Dereference:   retval = check_dereference((AstDereference *) expr); break;
+        case Ast_Kind_Slice:
+        case Ast_Kind_Subscript:     retval = check_subscript((AstSubscript **) pexpr); break;
+        case Ast_Kind_Field_Access:  retval = check_field_access((AstFieldAccess **) pexpr); break;
+        case Ast_Kind_Method_Call:   retval = check_method_call((AstBinaryOp **) pexpr); break;
+        case Ast_Kind_Size_Of:       retval = check_size_of((AstSizeOf *) expr); break;
+        case Ast_Kind_Align_Of:      retval = check_align_of((AstAlignOf *) expr); break;
+        case Ast_Kind_Range_Literal: retval = check_range_literal((AstRangeLiteral **) pexpr); break;
+
+        case Ast_Kind_Global:
+            if (expr->type == NULL) {
+                onyx_report_error(expr->token->pos, Error_Critical, "Global with unknown type.");
+                retval = Check_Error;
+            }
+            break;
+
+        case Ast_Kind_NumLit:
+            assert(expr->type != NULL);
+            break;
+
+        case Ast_Kind_Struct_Literal:
+            retval = check_struct_literal((AstStructLiteral *) expr);
+            break;
+
+        case Ast_Kind_Array_Literal:
+            retval = check_array_literal((AstArrayLiteral *) expr);
+            break;
+
+        case Ast_Kind_Function:
+            // NOTE: Will need something like this at some point
+            // AstFunction* func = (AstFunction *) expr;
+            // bh_arr_each(AstParam, param, func->params) {
+            //     if (param->default_value != NULL) {
+            //         onyx_message_add(Msg_Type_Literal,
+            //                 func->token->pos,
+            //                 "cannot use functions with default parameters in this way");
+            //         retval = 1;
+            //         break;
+            //     }
+            // }
+            if (expr->type == NULL)
+                YIELD(expr->token->pos, "Waiting for function type to be resolved.");
+
+            expr->flags |= Ast_Flag_Function_Used;
+            break;
+
+        case Ast_Kind_Directive_Solidify:
+            CHECK(directive_solidify, (AstDirectiveSolidify **) pexpr);
+            break;
+
+        case Ast_Kind_Directive_Defined:
+            *pexpr = (AstTyped *) make_bool_literal(context.ast_alloc, ((AstDirectiveDefined *) expr)->is_defined);
+            fill_in_type(*pexpr);
+            break;
+
+        case Ast_Kind_Compound:
+            CHECK(compound, (AstCompound *) expr);
+            break;
+
+        case Ast_Kind_Call_Site:
+            // NOTE: This has to be set here because if it were to be set in the parser,
+            // builtin_callsite_type wouldn't be known when parsing the builtin.onyx file.
+            expr->type_node = builtin_callsite_type;
+            break;
+
+        case Ast_Kind_If_Expression:
+            CHECK(if_expression, (AstIfExpression *) expr);
+            break;
+
+        case Ast_Kind_Alias:
+            CHECK(expression, &((AstAlias *) expr)->alias);
+            expr->flags |= (((AstAlias *) expr)->alias->flags & Ast_Flag_Comptime);
+            expr->type = ((AstAlias *) expr)->alias->type;
+            break;
+
+        case Ast_Kind_Directive_Insert:
+            retval = check_insert_directive((AstDirectiveInsert **) pexpr);
+            break;
+
+        case Ast_Kind_Code_Block:
+            expr->flags |= Ast_Flag_Comptime;
+            fill_in_type(expr);
+            break;
+
+        case Ast_Kind_Do_Block:
+            retval = check_do_block((AstDoBlock **) pexpr);
+            break;
+
+        case Ast_Kind_Memres:
+            if (expr->type == NULL) YIELD(expr->token->pos, "Waiting to know globals type.");
+            break;
+
+        case Ast_Kind_StrLit: break;
+        case Ast_Kind_File_Contents: break;
+        case Ast_Kind_Overloaded_Function: break;
+        case Ast_Kind_Enum_Value: break;
+        case Ast_Kind_Polymorphic_Proc: break;
+        case Ast_Kind_Package: break;
+        case Ast_Kind_Error: break;
+        case Ast_Kind_Unary_Field_Access: break;
+        case Ast_Kind_Constraint_Sentinel: break;
+        case Ast_Kind_Switch_Case: break;
+        case Ast_Kind_Foreign_Block: break;
+        case Ast_Kind_Zero_Value: break;
+
+        default:
+            retval = Check_Error;
+            onyx_report_error(expr->token->pos, Error_Critical, "UNEXPECTED INTERNAL COMPILER ERROR");
+            DEBUG_HERE;
+            break;
+    }
+
+    return retval;
+}
+
+CheckStatus check_global(AstGlobal* global) {
+    fill_in_type((AstTyped *) global);
+
+    if (global->type == NULL) {
+        YIELD(global->token->pos, "Trying to resolve type for global.");
+    }
+
+    return Check_Success;
+}
+
+CheckStatus check_insert_directive(AstDirectiveInsert** pinsert) {
+    AstDirectiveInsert* insert = *pinsert;
+    if (insert->flags & Ast_Flag_Has_Been_Checked) return Check_Success;
+
+    CHECK(expression, &insert->code_expr);
+    if (insert->code_expr->type == NULL) {
+        if (insert->code_expr->entity && insert->code_expr->entity->state >= Entity_State_Code_Gen) {
+            ERROR(insert->token->pos, "Expected expression of type 'Code'.");
+        }
+
+        // Bad wording for the message.
+        YIELD(insert->token->pos, "Waiting for resolution to code expression type.");
+    }
+
+    Type* code_type = type_build_from_ast(context.ast_alloc, builtin_code_type);
+
+    TYPE_CHECK(&insert->code_expr, code_type) {
+        ERROR_(insert->token->pos, "#unquote expected a value of type 'Code', got '%s'.",
+            type_get_name(insert->code_expr->type));
+    }
+
+    AstCodeBlock* code_block = (AstCodeBlock *) insert->code_expr;
+    code_block = (AstCodeBlock *) strip_aliases((AstNode *) code_block);
+
+    assert(code_block->kind == Ast_Kind_Code_Block);
+
+    AstNode* cloned_block = ast_clone(context.ast_alloc, code_block->code);
+    cloned_block->next = insert->next;
+    *(AstNode **) pinsert = cloned_block;
+
+    insert->flags |= Ast_Flag_Has_Been_Checked;
+
+    return Check_Return_To_Symres;
+}
+
+CheckStatus check_directive_solidify(AstDirectiveSolidify** psolid) {
+    AstDirectiveSolidify* solid = *psolid;
+
+    bh_arr_each(AstPolySolution, sln, solid->known_polyvars) {
+        CHECK(expression, &sln->value);
+
+        if (node_is_type((AstNode *) sln->value)) {
+            sln->type = type_build_from_ast(context.ast_alloc, sln->ast_type);
+            sln->kind = PSK_Type;
+        } else {
+            sln->kind = PSK_Value;
+        }
+    }
+
+    solid->resolved_proc = polymorphic_proc_try_solidify(solid->poly_proc, solid->known_polyvars, solid->token);
+    if (solid->resolved_proc == (AstNode *) &node_that_signals_a_yield) {
+        solid->resolved_proc = NULL;
+        YIELD(solid->token->pos, "Waiting for partially solidified procedure.");
+    }
+
+    // NOTE: Not a DirectiveSolidify.
+    *psolid = (AstDirectiveSolidify *) solid->resolved_proc;
+
+    return Check_Success;
+}
+
+CheckStatus check_remove_directive(AstDirectiveRemove *remove) {
+    if (!inside_for_iterator) {
+        ERROR(remove->token->pos, "#remove is only allowed in the body of a for-loop over an iterator.");
+    }
+
+    return Check_Success;
+}
+
+CheckStatus check_statement(AstNode** pstmt) {
+    AstNode* stmt = *pstmt;
+
+    current_checking_level = STATEMENT_LEVEL;
+
+    switch (stmt->kind) {
+        case Ast_Kind_Jump:       return Check_Success;
+
+        case Ast_Kind_Return:     return check_return((AstReturn *) stmt);
+        case Ast_Kind_If:         return check_if((AstIfWhile *) stmt);
+        case Ast_Kind_Static_If:  return check_if((AstIfWhile *) stmt);
+        case Ast_Kind_While:      return check_while((AstIfWhile *) stmt);
+        case Ast_Kind_For:        return check_for((AstFor *) stmt);
+        case Ast_Kind_Switch:     return check_switch((AstSwitch *) stmt);
+        case Ast_Kind_Block:      return check_block((AstBlock *) stmt);
+        case Ast_Kind_Defer:      return check_statement(&((AstDefer *) stmt)->stmt);
+        case Ast_Kind_Directive_Remove: return check_remove_directive((AstDirectiveRemove *) stmt);
+        case Ast_Kind_Call: {
+            CHECK(call, (AstCall **) pstmt);
+            (*pstmt)->flags |= Ast_Flag_Expr_Ignored;
+            return Check_Success;
+        }
+
+        case Ast_Kind_Binary_Op:
+            CHECK(binaryop, (AstBinaryOp **) pstmt);
+            (*pstmt)->flags |= Ast_Flag_Expr_Ignored;
+            return Check_Success;
+
+        // NOTE: Local variable declarations used to be removed after the symbol
+        // resolution phase because long long ago, all locals needed to be known
+        // in a block in order to efficiently allocate enough space and registers
+        // for them all. Now with LocalAllocator, this is no longer necessary.
+        // Therefore, locals stay in the tree and need to be passed along.
+        case Ast_Kind_Local: {
+            AstTyped* typed_stmt = (AstTyped *) stmt;
+            fill_in_type(typed_stmt);
+            if (typed_stmt->type_node != NULL && typed_stmt->type == NULL) {
+                CHECK(type, &typed_stmt->type_node);
+
+                if (!node_is_type((AstNode *) typed_stmt->type_node)) {
+                    ERROR(stmt->token->pos, "Local's type is not a type.");
+                }
+
+                YIELD(typed_stmt->token->pos, "Waiting for local variable's type.");
+            }
+
+            if (typed_stmt->next != NULL && typed_stmt->next->kind == Ast_Kind_Binary_Op) {
+                AstBinaryOp *next = (AstBinaryOp *) typed_stmt->next;
+                if (next->operation == Binary_Op_Assign && next->left == typed_stmt) {
+                    typed_stmt->flags |= Ast_Flag_Decl_Followed_By_Init;
+                }
+            }
+            return Check_Success;
+        }
+
+        default:
+            CHECK(expression, (AstTyped **) pstmt);
+            (*pstmt)->flags |= Ast_Flag_Expr_Ignored;
+            return Check_Success;
+    }
+}
+
+CheckStatus check_statement_chain(AstNode** start) {
+    while (*start) {
+        CHECK(statement, start);
+        start = &(*start)->next;
+    }
+
+    return Check_Success;
+}
+
+CheckStatus check_block(AstBlock* block) {
+    // This used to use statement_chain, but since block optimize which statements need to be rechecked,
+    // it has to be its own thing.
+
+    AstNode** start = &block->body;
+    fori (i, 0, block->statement_idx) {
+        start = &(*start)->next;
+    }
+
+    while (*start) {
+        CheckStatus cs = check_statement(start);
+        switch (cs) {
+            case Check_Success:
+                start = &(*start)->next;
+                block->statement_idx++;
+                break;
+
+            case Check_Return_To_Symres:
+                block->statement_idx = 0;
+
+            default:
+                return cs;
+        }
+
+    }
+
+    return Check_Success;
+}
+
+CheckStatus check_function(AstFunction* func) {
+    if (func->flags & Ast_Flag_Has_Been_Checked) return Check_Success;
+    if (func->entity_header && func->entity_header->state < Entity_State_Code_Gen)
+        YIELD(func->token->pos, "Waiting for procedure header to pass type-checking");
+
+    bh_arr_each(AstTyped *, pexpr, func->tags) {
+        CHECK(expression, pexpr);
+
+        if (((*pexpr)->flags & Ast_Flag_Comptime) == 0) {
+            ERROR((*pexpr)->token->pos, "#tag expressions should be compile time known.");
+        }
+    }
+
+    inside_for_iterator = 0;
+    expected_return_type = &func->type->Function.return_type;
+    if (func->body) {
+        CheckStatus status = check_block(func->body);
+        if (status == Check_Error && func->generated_from && context.cycle_detected == 0)
+            ERROR(func->generated_from->pos, "Error in polymorphic procedure generated from this location.");
+
+        if (status != Check_Success) {
+            expected_return_type = NULL;
+            return status;
+        }
+    }
+
+    if (*expected_return_type == &type_auto_return) {
+        *expected_return_type = &basic_types[Basic_Kind_Void];
+    }
+
+    func->flags |= Ast_Flag_Has_Been_Checked;
+    return Check_Success;
+}
+
+CheckStatus check_overloaded_function(AstOverloadedFunction* func) {
+    b32 done = 1;
+
+    bh_imap all_overloads;
+    bh_imap_init(&all_overloads, global_heap_allocator, 4);
+    build_all_overload_options(func->overloads, &all_overloads);
+
+    bh_arr_each(bh__imap_entry, entry, all_overloads.entries) {
+        AstTyped* node = (AstTyped *) entry->key;
+        if (node->kind == Ast_Kind_Overloaded_Function) continue;
+
+        if (   node->kind != Ast_Kind_Function
+            && node->kind != Ast_Kind_Polymorphic_Proc
+            && node->kind != Ast_Kind_Macro) {
+            onyx_report_error(node->token->pos, Error_Critical, "Overload option not procedure or macro. Got '%s'",
+                onyx_ast_node_kind_string(node->kind));
+
+            bh_imap_free(&all_overloads);
+            return Check_Error;
+        }
+
+        if (node->kind == Ast_Kind_Function) {
+            AstFunction* func = (AstFunction *) node;
+
+            if (func->entity_header && func->entity_header->state <= Entity_State_Check_Types) {
+                done = 0;
+            }
+        }
+    }
+
+    bh_imap_free(&all_overloads);
+
+    if (done) return Check_Success;
+    else      YIELD(func->token->pos, "Waiting for all options to pass type-checking.");
+}
+
+CheckStatus check_struct(AstStructType* s_node) {
+    if (s_node->entity_defaults && s_node->entity_defaults->state < Entity_State_Check_Types)
+        YIELD(s_node->token->pos, "Waiting for struct member defaults to pass symbol resolution.");
+
+    if (s_node->min_size_)      CHECK(expression, &s_node->min_size_);
+    if (s_node->min_alignment_) CHECK(expression, &s_node->min_alignment_);
+
+    if (s_node->polymorphic_argument_types) {
+        assert(s_node->polymorphic_arguments);
+
+        fori (i, 0, (i64) bh_arr_length(s_node->polymorphic_argument_types)) {
+            Type *arg_type = type_build_from_ast(context.ast_alloc, s_node->polymorphic_argument_types[i]);
+            if (arg_type == NULL) YIELD(s_node->polymorphic_argument_types[i]->token->pos, "Waiting to build type for polymorph argument.");
+
+            // CLEANUP: This might be wrong...
+            if (s_node->polymorphic_arguments[i].value) {
+                TYPE_CHECK(&s_node->polymorphic_arguments[i].value, arg_type) {
+                    ERROR_(s_node->polymorphic_arguments[i].value->token->pos, "Expected value of type %s, got %s.",
+                        type_get_name(arg_type),
+                        type_get_name(s_node->polymorphic_arguments[i].value->type));
+                }
+            }
+        }
+    }
+
+    if (s_node->constraints.constraints) {
+        s_node->constraints.produce_errors = (s_node->flags & Ast_Flag_Header_Check_No_Error) == 0;
+
+        OnyxFilePos pos = s_node->token->pos;
+        if (s_node->polymorphic_error_loc.filename) {
+            pos = s_node->polymorphic_error_loc;
+        }
+        CHECK(constraint_context, &s_node->constraints, s_node->scope, pos);
+    }
+
+    //
+    // This ensures that all procedures defined inside of a structure are
+    // not pruned and omitted from the binary. This is because a large
+    // use-case of procedures in structures is dynamically linking them
+    // using the type info data.
+    if (s_node->scope) {
+        fori (i, 0, shlen(s_node->scope->symbols)) {
+            AstNode* node = s_node->scope->symbols[i].value;
+            if (node->kind == Ast_Kind_Function) {
+                node->flags |= Ast_Flag_Function_Used;
+            }
+        }
+    }
+
+    bh_arr_each(AstStructMember *, smem, s_node->members) {
+        if ((*smem)->type_node != NULL) {
+            CHECK(type, &(*smem)->type_node);
+        }
+
+        if ((*smem)->type_node == NULL && (*smem)->initial_value != NULL) {
+            CHECK(expression, &(*smem)->initial_value);
+
+            fill_in_type((*smem)->initial_value);
+            if ((*smem)->initial_value->type == NULL)
+                YIELD((*smem)->initial_value->token->pos, "Trying to resolve type for initial value for member.");
+
+            resolve_expression_type((*smem)->initial_value);
+            if ((*smem)->type == NULL) (*smem)->type = (*smem)->initial_value->type;
+
+            if ((*smem)->type == NULL) {
+                ERROR((*smem)->initial_value->token->pos, "Unable to deduce type of initial value. This is probably a compiler bug.");
+            }
+        }
+    }
+
+    // NOTE: fills in the pending_type.
+    s_node->ready_to_build_type = 1;
+    type_build_from_ast(context.ast_alloc, (AstType *) s_node);
+    if (s_node->pending_type == NULL || !s_node->pending_type_is_valid)
+        YIELD(s_node->token->pos, "Waiting for type to be constructed.");
+
+    bh_arr_each(StructMember *, smem, s_node->pending_type->Struct.memarr) {
+        if ((*smem)->type->kind == Type_Kind_Compound) {
+            ERROR(s_node->token->pos, "Compound types are not allowed as struct member types.");
+        }
+
+        if ((*smem)->used) {
+            if (!type_struct_member_apply_use(context.ast_alloc, s_node->pending_type, *smem)) {
+                YIELD((*smem)->token->pos, "Waiting for use to be applied.");
+            }
+        }
+    }
+
+    s_node->stcache = s_node->pending_type;
+    s_node->stcache->Struct.status = SPS_Uses_Done;
+
+    return Check_Success;
+}
+
+CheckStatus check_struct_defaults(AstStructType* s_node) {
+    if (s_node->entity_type && s_node->entity_type->state < Entity_State_Code_Gen)
+        YIELD(s_node->token->pos, "Waiting for struct type to be constructed before checking defaulted members.");
+    if (s_node->entity_type && s_node->entity_type->state == Entity_State_Failed)
+        return Check_Failed;
+
+    if (s_node->meta_tags) {
+        bh_arr_each(AstTyped *, meta, s_node->meta_tags) {
+            CHECK(expression, meta);
+            resolve_expression_type(*meta);
+
+            if (((*meta)->flags & Ast_Flag_Comptime) == 0) {
+                onyx_report_error((*meta)->token->pos, Error_Critical, "#tag expressions are expected to be compile-time known.");
+                return Check_Error;
+            }
+        }
+    }
+
+    bh_arr_each(StructMember *, smem, s_node->stcache->Struct.memarr) {
+        if ((*smem)->initial_value && *(*smem)->initial_value) {
+            CHECK(expression, (*smem)->initial_value);
+
+            TYPE_CHECK((*smem)->initial_value, (*smem)->type) {
+                ERROR_((*(*smem)->initial_value)->token->pos,
+                        "Mismatched type for initial value, expected '%s', got '%s'.",
+                        type_get_name((*smem)->type),
+                        type_get_name((*(*smem)->initial_value)->type));
+            }
+
+            resolve_expression_type(*(*smem)->initial_value);
+        }
+
+        if ((*smem)->meta_tags) {
+            bh_arr_each(AstTyped *, meta, (*smem)->meta_tags) {
+                CHECK(expression, meta);
+                resolve_expression_type(*meta);
+
+                if (((*meta)->flags & Ast_Flag_Comptime) == 0) {
+                    onyx_report_error((*meta)->token->pos, Error_Critical, "#tag expressions are expected to be compile-time known.");
+                    return Check_Error;
+                }
+            }
+        }
+    }
+
+    return Check_Success;
+}
+
+CheckStatus check_temp_function_header(AstFunction* func) {
+    CheckStatus cs = check_function_header(func);
+    if (cs == Check_Error) {
+        if (func->flags & Ast_Flag_Header_Check_No_Error) {
+            onyx_clear_errors();
+        }
+
+        return Check_Failed;
+    }
+
+    if (cs != Check_Success) return cs;
+
+    return Check_Complete;
+}
+
+CheckStatus check_function_header(AstFunction* func) {
+    //if (func->entity_body && func->entity_body->state < Entity_State_Check_Types)
+    //    YIELD(func->token->pos, "Waiting for function body to complete symbol resolution to check header.");
+
+    b32 expect_default_param = 0;
+    b32 has_had_varargs = 0;
+
+    if (func->constraints.constraints != NULL && func->constraints.constraints_met == 0) {
+        func->constraints.produce_errors = (func->flags & Ast_Flag_Header_Check_No_Error) == 0;
+        CHECK(constraint_context, &func->constraints, func->scope, func->token->pos);
+
+        // All constraints have been met. Return to symbol resolution to finish
+        // looking up all symbols in the function.
+        return Check_Return_To_Symres;
+    }
+
+    bh_arr_each(AstParam, param, func->params) {
+        AstLocal* local = param->local;
+
+        if (expect_default_param && param->default_value == NULL) {
+            ERROR(local->token->pos,
+                    "All parameters must have default values after the first default valued parameter.");
+        }
+
+        if (has_had_varargs && param->vararg_kind != VA_Kind_Not_VA) {
+            ERROR(local->token->pos,
+                    "Can only have one param that is of variable argument type.");
+        }
+
+        if (has_had_varargs && param->vararg_kind != VA_Kind_Not_VA) {
+            ERROR(local->token->pos,
+                    "Variable arguments must be last in parameter list");
+        }
+
+        if (param->vararg_kind == VA_Kind_Untyped) {
+            // HACK
+            if (builtin_vararg_type_type == NULL)
+                builtin_vararg_type_type = type_build_from_ast(context.ast_alloc, builtin_vararg_type);
+
+            local->type = builtin_vararg_type_type;
+        }
+
+        if (param->default_value != NULL) {
+            if (param->vararg_kind != VA_Kind_Not_VA) {
+                ERROR(local->token->pos, "Variadic arguments cannot have default values.");
+            }
+
+            CHECK(expression, &param->default_value);
+
+            if (local->type_node == NULL && local->type == NULL) {
+                local->type = resolve_expression_type(param->default_value);
+            }
+
+            expect_default_param = 1;
+        }
+
+        if (local->type_node != NULL) {
+            // If the function has the no_error flag, then the type node should have it set too.
+            // This allows for polymorphic structures with constraints to fail gracefully.
+            local->type_node->flags |= (func->flags & Ast_Flag_Header_Check_No_Error);
+            CHECK(type, &local->type_node);
+        }
+
+        fill_in_type((AstTyped *) local);
+        if (local->type == NULL) {
+            YIELD(local->token->pos, "Waiting for parameter type to be known.");
+        }
+
+        if (local->type == (Type *) &node_that_signals_failure) {
+            return Check_Failed;
+        }
+
+        if (local->type->kind == Type_Kind_Compound) {
+            ERROR(param->local->token->pos, "Compound types are not allowed as parameter types. Try splitting this into multiple parameters.");
+        }
+
+        // NOTE: I decided to make parameter default values not type checked against
+        // the actual parameter type. The actual type checking will happen in check_call
+        // when the default value is used as an argument and then has to be checked against
+        // the parameter type                                  - brendanfh 2021/01/06
+        // if (param->default_value != NULL) {
+        //     if (!unify_node_and_type(&param->default_value, param->local->type)) {
+        //         onyx_report_error(param->local->token->pos,
+        //                 "Expected default value of type '%s', was of type '%s'.",
+        //                 type_get_name(param->local->type),
+        //                 type_get_name(param->default_value->type));
+        //         return Check_Error;
+        //     }
+        // }
+
+        if (param->vararg_kind != VA_Kind_Not_VA) has_had_varargs = 1;
+
+        if (local->type->kind != Type_Kind_Array && type_size_of(local->type) == 0) {
+            ERROR(local->token->pos, "Function parameters cannot have zero-width types.");
+        }
+    }
+
+    if (func->return_type != NULL) CHECK(type, &func->return_type);
+
+    func->type = type_build_function_type(context.ast_alloc, func);
+    if (func->type == NULL) YIELD(func->token->pos, "Waiting for function type to be constructed");
+
+    return Check_Success;
+}
+
+CheckStatus check_memres_type(AstMemRes* memres) {
+    CHECK(type, &memres->type_node);
+    fill_in_type((AstTyped *) memres);
+    if (memres->type_node && !memres->type) YIELD(memres->token->pos, "Waiting for global type to be constructed.");
+    return Check_Success;
+}
+
+CheckStatus check_memres(AstMemRes* memres) {
+    assert(memres->type_entity);
+    if (memres->type_entity->state < Entity_State_Code_Gen) YIELD(memres->token->pos, "Waiting for global to pass type construction.");
+
+    if (memres->initial_value != NULL) {
+        if (memres->threadlocal) {
+            onyx_report_error(memres->token->pos, Error_Critical, "'#thread_local' variables cannot have an initializer at the moment.");
+            return Check_Error;
+        }
+
+        CHECK(expression, &memres->initial_value);
+
+        if (memres->type != NULL) {
+            Type* memres_type = memres->type;
+            TYPE_CHECK(&memres->initial_value, memres_type) {
+                ERROR_(memres->token->pos,
+                        "Cannot assign value of type '%s' to a '%s'.",
+                        node_get_type_name(memres->initial_value),
+                        type_get_name(memres_type));
+            }
+
+        } else {
+            resolve_expression_type(memres->initial_value);
+            if (memres->initial_value->type == NULL && memres->initial_value->entity != NULL && memres->initial_value->entity->state <= Entity_State_Check_Types) {
+                YIELD(memres->token->pos, "Waiting for global type to be constructed.");
+            }
+            memres->type = memres->initial_value->type;
+        }
+
+        if ((memres->initial_value->flags & Ast_Flag_Comptime) == 0) {
+            if (memres->initial_value->entity != NULL && memres->initial_value->entity->state <= Entity_State_Check_Types) {
+                YIELD(memres->token->pos, "Waiting for initial value to be checked.");
+            }
+
+            ERROR(memres->initial_value->token->pos, "Top level expressions must be compile time known.");
+        }
+    }
+
+    return Check_Success;
+}
+
+CheckStatus check_type(AstType** ptype) {
+    if (ptype == NULL || *ptype == NULL) return Check_Success;
+
+    AstType* type = *ptype;
+    AstType* original_type = type;
+    while (type->kind == Ast_Kind_Type_Alias)
+        type = ((AstTypeAlias *) type)->to;
+
+    if (type->flags & Ast_Flag_Has_Been_Checked) return Check_Success;
+
+    switch (type->kind) {
+        case Ast_Kind_Poly_Call_Type: {
+            AstPolyCallType* pc_node = (AstPolyCallType *) type;
+
+            bh_arr_each(AstNode *, param, pc_node->params) {
+                if (!node_is_type(*param)) {
+                    CHECK(expression, (AstTyped **) param);
+                    resolve_expression_type((AstTyped *) *param);
+                    fill_in_type((AstTyped *) *param);
+                }
+            }
+
+            break;
+        }
+
+        case Ast_Kind_Typeof: {
+            AstTypeOf *type_of = (AstTypeOf *) type;
+            CHECK(expression, (AstTyped **) &type_of->expr);
+            resolve_expression_type(type_of->expr);
+
+            if (type_of->expr->type == NULL) {
+                YIELD(type_of->token->pos, "Trying to check type for type-of expression.");
+            }
+
+            type_of->resolved_type = type_of->expr->type;
+            break;
+        }
+
+        case Ast_Kind_Pointer_Type: ((AstPointerType *) type)->elem->flags |= type->flags & Ast_Flag_Header_Check_No_Error; CHECK(type, &((AstPointerType *) type)->elem); break;
+        case Ast_Kind_Slice_Type:   ((AstSliceType *) type)->elem->flags |= type->flags & Ast_Flag_Header_Check_No_Error; CHECK(type, &((AstSliceType *) type)->elem); break;
+        case Ast_Kind_DynArr_Type:  ((AstDynArrType *) type)->elem->flags |= type->flags & Ast_Flag_Header_Check_No_Error; CHECK(type, &((AstDynArrType *) type)->elem); break;
+        case Ast_Kind_VarArg_Type:  ((AstVarArgType *) type)->elem->flags |= type->flags & Ast_Flag_Header_Check_No_Error; CHECK(type, &((AstVarArgType *) type)->elem); break;
+
+        case Ast_Kind_Function_Type: {
+            AstFunctionType* ftype = (AstFunctionType *) type;
+
+            CHECK(type, &ftype->return_type);
+
+            if (ftype->param_count > 0) {
+                fori (i, 0, (i64) ftype->param_count) {
+                    CHECK(type, &ftype->params[i]);
+                }
+            }
+            break;
+        }
+
+        case Ast_Kind_Type_Compound: {
+            AstCompoundType* ctype = (AstCompoundType *) type;
+
+            bh_arr_each(AstType *, type, ctype->types) CHECK(type, type);
+            break;
+        }
+
+        case Ast_Kind_Array_Type: {
+            AstArrayType* atype = (AstArrayType *) type;
+            if (atype->count_expr) {
+                CHECK(expression, &atype->count_expr);
+                resolve_expression_type(atype->count_expr);
+            }
+
+            break;
+        }
+
+        case Ast_Kind_Field_Access: {
+            CHECK(field_access, (AstFieldAccess **) ptype);
+            type = *ptype;
+            original_type = type;
+
+            if (!node_is_type((AstNode *) type)) {
+                ERROR_(original_type->token->pos, "This field access did not resolve to be a type. It resolved to be a '%s'.", onyx_ast_node_kind_string(type->kind));
+            }
+            break;
+        }
+    }
+
+    type = original_type;
+    type->flags |= Ast_Flag_Comptime;
+    while (type->kind == Ast_Kind_Type_Alias) {
+        type->flags |= Ast_Flag_Comptime;
+        type = ((AstTypeAlias *) type)->to;
+    }
+
+    type->flags |= Ast_Flag_Has_Been_Checked;
+    return Check_Success;
+}
+
+CheckStatus check_static_if(AstIf* static_if) {
+    expression_types_must_be_known = 1;
+    CheckStatus result = check_expression(&static_if->cond);
+    expression_types_must_be_known = 0;
+    if (result == Check_Yield_Macro) return Check_Yield_Macro;
+
+    if (result > Check_Errors_Start || !(static_if->cond->flags & Ast_Flag_Comptime)) {
+        ERROR(static_if->token->pos, "Expected this condition to be compile time known.");
+    }
+
+    if (!type_is_bool(static_if->cond->type)) {
+        ERROR(static_if->token->pos, "Expected this condition to be a boolean value.");
+    }
+
+    static_if->flags |= Ast_Flag_Static_If_Resolved;
+
+    b32 resolution = static_if_resolution(static_if);
+
+    if (context.options->print_static_if_results)
+        bh_printf("Static if statement at %s:%d:%d resulted in %s\n",
+            static_if->token->pos.filename,
+            static_if->token->pos.line,
+            static_if->token->pos.column,
+            resolution ? "true" : "false");
+
+    if (resolution) {
+        bh_arr_each(Entity *, ent, static_if->true_entities) {
+            entity_heap_insert_existing(&context.entities, *ent);
+        }
+
+    } else {
+        bh_arr_each(Entity *, ent, static_if->false_entities) {
+            entity_heap_insert_existing(&context.entities, *ent);
+        }
+    }
+
+    return Check_Complete;
+}
+
+CheckStatus check_process_directive(AstNode* directive) {
+    if (directive->kind == Ast_Kind_Directive_Export) {
+        AstDirectiveExport *export = (AstDirectiveExport *) directive;
+        AstTyped *exported = export->export;
+        if (exported->entity && exported->entity->state <= Entity_State_Check_Types)
+            YIELD(directive->token->pos, "Waiting for exported type to be known.");
+
+        CHECK(expression, &export->export_name_expr);
+
+        if (export->export_name_expr->kind != Ast_Kind_StrLit) {
+            ERROR_(export->token->pos, "Expected export name to be a string literal, got '%s'.", onyx_ast_node_kind_string(export->export_name_expr->kind));
+        }
+
+        export->export_name = export->export_name_expr->token;
+    }
+
+    if (directive->kind == Ast_Kind_Directive_Init) {
+        AstDirectiveInit *init = (AstDirectiveInit *) directive;
+        if ((init->flags & Ast_Flag_Has_Been_Checked) == 0) {
+            CHECK(expression, &init->init_proc);
+
+            if (init->init_proc->kind != Ast_Kind_Function) {
+                ERROR_(init->token->pos, "#init only works for functions, got '%s'", onyx_ast_node_kind_string(init->init_proc->kind));
+            }
+
+            assert(init->init_proc->type);
+            if (init->init_proc->type->Function.param_count != 0) {
+                ERROR(init->token->pos, "#init expects a function that takes 0 arguments.");
+            }
+        }
+
+        init->flags |= Ast_Flag_Has_Been_Checked;
+
+        if (init->dependencies) {
+            i32 i = 0;
+            bh_arr_each(AstDirectiveInit *, dependency, init->dependencies) {
+                AstTyped *d = (AstTyped *) strip_aliases((AstNode *) *dependency);
+                if (d->kind != Ast_Kind_Directive_Init) {
+                    ERROR_(init->token->pos, "All dependencies of an #init must be another #init. The %d%s dependency was not.", i + 1, bh_num_suffix(i + 1));
+                }
+
+                assert(d->entity);
+                if (d->entity->state != Entity_State_Finalized) {
+                    YIELD(init->token->pos, "Circular dependency in #init nodes. Here are the nodes involved.");
+                }
+
+                i++;
+            }
+        }
+
+        bh_arr_push(init_procedures, (AstFunction *) init->init_proc);
+        return Check_Complete;
+    }
+
+    if (directive->kind == Ast_Kind_Directive_Library) {
+        AstDirectiveLibrary *library = (AstDirectiveLibrary *) directive;
+
+        if (library->library_symbol->kind != Ast_Kind_StrLit) {
+            ERROR_(library->token->pos, "#library directive expected compile-time known string for library name. Got '%s'.",
+                onyx_ast_node_kind_string(library->library_symbol->kind));
+        }
+
+        AstStrLit *symbol = (AstStrLit *) library->library_symbol;
+        char* temp_name     = bh_alloc_array(global_scratch_allocator, char, symbol->token->length);
+        i32   temp_name_len = string_process_escape_seqs(temp_name, symbol->token->text, symbol->token->length);
+        library->library_name = bh_strdup(global_heap_allocator, temp_name);
+        return Check_Success;
+    }
+
+    return Check_Success;
+}
+
+CheckStatus check_macro(AstMacro* macro) {
+    if (macro->body->kind == Ast_Kind_Function) {
+        CHECK(function_header, (AstFunction *) macro->body);
+    }
+
+    return Check_Success;
+}
+
+CheckStatus check_constraint(AstConstraint *constraint) {
+    switch (constraint->phase) {
+        case Constraint_Phase_Cloning_Expressions: {
+            if (constraint->interface->kind == Ast_Kind_Symbol) {
+                return Check_Return_To_Symres;
+            }
+
+            if (constraint->interface->kind != Ast_Kind_Interface) {
+                // CLEANUP: This error message might not look totally right in some cases.
+                ERROR_(constraint->token->pos, "'%b' is not an interface. It is a '%s'.",
+                    constraint->token->text, constraint->token->length,
+                    onyx_ast_node_kind_string(constraint->interface->kind));
+            }
+
+            bh_arr_new(global_heap_allocator, constraint->exprs, bh_arr_length(constraint->interface->exprs));
+            bh_arr_each(InterfaceConstraint, ic, constraint->interface->exprs) {
+                InterfaceConstraint new_ic = {0};
+                new_ic.expr = (AstTyped *) ast_clone(context.ast_alloc, (AstNode *) ic->expr);
+                new_ic.expected_type_expr = (AstType *) ast_clone(context.ast_alloc, (AstNode *) ic->expected_type_expr);
+                new_ic.invert_condition = ic->invert_condition;
+                bh_arr_push(constraint->exprs, new_ic);
+            }
+
+            assert(constraint->interface->entity && constraint->interface->entity->scope);
+
+            constraint->scope = scope_create(context.ast_alloc, constraint->interface->entity->scope, constraint->token->pos);
+
+            fori (i, 0, bh_arr_length(constraint->interface->params)) {
+                InterfaceParam *ip = &constraint->interface->params[i];
+
+                AstTyped *sentinel = onyx_ast_node_new(context.ast_alloc, sizeof(AstTyped), Ast_Kind_Constraint_Sentinel);
+                sentinel->token = ip->value_token;
+                sentinel->type_node = constraint->type_args[i];
+
+                AstAlias *type_alias = onyx_ast_node_new(context.ast_alloc, sizeof(AstAlias), Ast_Kind_Alias);
+                type_alias->token = ip->type_token;
+                type_alias->alias = (AstTyped *) constraint->type_args[i];
+
+                symbol_introduce(constraint->scope, ip->value_token, (AstNode *) sentinel);
+                symbol_introduce(constraint->scope, ip->type_token, (AstNode *) type_alias);
+            }
+
+            assert(constraint->entity);
+            constraint->entity->scope = constraint->scope;
+
+            constraint->phase = Constraint_Phase_Checking_Expressions;
+            return Check_Return_To_Symres;
+        }
+
+        case Constraint_Phase_Checking_Expressions: {
+            fori (i, constraint->expr_idx, bh_arr_length(constraint->exprs)) {
+                InterfaceConstraint* ic = &constraint->exprs[i];
+
+                CheckStatus cs = check_expression(&ic->expr);
+                if (cs == Check_Return_To_Symres || cs == Check_Yield_Macro) {
+                    return cs;
+                }
+
+                if (cs == Check_Error && !ic->invert_condition) {
+                    goto constraint_error;
+                }
+
+                if (cs == Check_Success && ic->invert_condition) {
+                    goto constraint_error;
+                }
+
+                if (ic->expected_type_expr) {
+                    cs = check_type(&ic->expected_type_expr);
+                    if (cs == Check_Return_To_Symres || cs == Check_Yield_Macro) {
+                        return cs;
+                    }
+
+                    ic->expected_type = type_build_from_ast(context.ast_alloc, ic->expected_type_expr);
+                    if (ic->expected_type == NULL) {
+                        YIELD(ic->expected_type_expr->token->pos, "Waiting on expected type expression to be resolved.");
+                    }
+
+                    TYPE_CHECK(&ic->expr, ic->expected_type) {
+                        if (!ic->invert_condition)
+                            goto constraint_error;
+                    }
+                }
+
+                constraint->expr_idx++;
+                continue;
+
+              constraint_error:
+                // HACK HACK HACK
+                onyx_clear_errors();
+                *constraint->report_status = Constraint_Check_Status_Failed;
+                return Check_Failed;
+            }
+
+            // HACK HACK HACK
+            onyx_clear_errors();
+            *constraint->report_status = Constraint_Check_Status_Success;
+            return Check_Complete;
+        }
+    }
+
+    return Check_Success;
+}
+
+CheckStatus check_constraint_context(ConstraintContext *cc, Scope *scope, OnyxFilePos pos) {
+    if (cc->constraint_checks) {
+        if (cc->constraints_met == 1) return Check_Success;
+
+        fori (i, 0, bh_arr_length(cc->constraints)) {
+            if (cc->constraint_checks[i] == Constraint_Check_Status_Failed) {
+                if (cc->produce_errors) {
+                    AstConstraint *constraint = cc->constraints[i];
+                    char constraint_map[512] = {0};
+                    fori (i, 0, bh_arr_length(constraint->type_args)) {
+                        if (i != 0) strncat(constraint_map, ", ", 511);
+
+                        OnyxToken* symbol = constraint->interface->params[i].value_token;
+                        token_toggle_end(symbol);
+                        strncat(constraint_map, symbol->text, 511);
+                        token_toggle_end(symbol);
+
+                        strncat(constraint_map, " is of type '", 511);
+                        strncat(constraint_map, type_get_name(type_build_from_ast(context.ast_alloc, constraint->type_args[i])), 511);
+                        strncat(constraint_map, "'", 511);
+                    }
+
+                    onyx_report_error(constraint->exprs[constraint->expr_idx].expr->token->pos, Error_Critical, "Failed to satisfy constraint where %s.", constraint_map);
+                    onyx_report_error(constraint->token->pos, Error_Critical, "Here is where the interface was used.");
+                    onyx_report_error(pos, Error_Critical, "Here is the code that caused this constraint to be checked.");
+
+                    return Check_Error;
+
+                } else {
+                    // If no error are suppose to be produced, we still need to signal that
+                    // the node reached a completed state.
+                    return Check_Failed;
+                }
+            }
+
+            if (cc->constraint_checks[i] == Constraint_Check_Status_Queued) {
+                YIELD(pos, "Waiting for constraints to be checked.");
+            }
+        }
+
+        cc->constraints_met = 1;
+        return Check_Success;
+
+    } else {
+        u32 count = bh_arr_length(cc->constraints);
+        ConstraintCheckStatus *ccs = bh_alloc_array(context.ast_alloc, ConstraintCheckStatus, count);
+
+        cc->constraint_checks = ccs;
+
+        fori (i, 0, count) {
+            ccs[i] = Constraint_Check_Status_Queued;
+            cc->constraints[i]->report_status = &ccs[i];
+            cc->constraints[i]->phase = Constraint_Phase_Cloning_Expressions;
+
+            add_entities_for_node(NULL, (AstNode *) cc->constraints[i], scope, NULL);
+        }
+
+        return Check_Yield_Macro;
+    }
+}
+
+CheckStatus check_polyquery(AstPolyQuery *query) {
+    if (query->function_header->scope == NULL)
+        query->function_header->scope = scope_create(context.ast_alloc, query->proc->parent_scope_of_poly_proc, query->token->pos);
+
+    CheckStatus header_check = check_temp_function_header(query->function_header);
+    if (header_check == Check_Return_To_Symres) return Check_Return_To_Symres;
+
+    b32 solved_something = 0;
+    i32 solved_count = 0;
+    char *err_msg = NULL;
+    bh_arr_each(AstPolyParam, param, query->proc->poly_params) {
+        AstPolySolution sln;
+        bh_arr_each(AstPolySolution, solved_sln, query->slns) {
+            if (token_equals(param->poly_sym->token, solved_sln->poly_sym->token)) {
+                goto poly_query_done;
+            }
+        }
+
+        // CLEANUP: I think this can go away because it is already done in polymorph.c
+        bh_arr_each(AstPolySolution, known_sln, query->proc->known_slns) {
+            if (token_equals(param->poly_sym->token, known_sln->poly_sym->token)) {
+                sln = *known_sln;
+                goto poly_var_solved;
+            }
+        }
+
+        TypeMatch result = find_polymorphic_sln(&sln, param, query->function_header, query->pp_lookup, query->given, &err_msg);
+
+        switch (result) {
+            case TYPE_MATCH_SUCCESS:
+                goto poly_var_solved;
+
+            case TYPE_MATCH_SPECIAL:
+                return Check_Yield_Macro;
+
+            case TYPE_MATCH_YIELD:
+            case TYPE_MATCH_FAILED: {
+                if (query->successful_symres || solved_something) continue;
+
+                if (query->error_on_fail || context.cycle_detected) {
+                    onyx_report_error(query->token->pos, Error_Critical, "Error solving for polymorphic variable '%b'.", param->poly_sym->token->text, param->poly_sym->token->length);
+                    if (err_msg != NULL)  onyx_report_error(query->token->pos, Error_Critical, "%s", err_msg);
+                    if (query->error_loc) onyx_report_error(query->error_loc->pos, Error_Critical, "Here is where the call is located."); // :ErrorMessage
+                }
+
+                return Check_Failed;
+            }
+        }
+
+poly_var_solved:
+        solved_something = 1;
+        bh_arr_push(query->slns, sln);
+        insert_poly_sln_into_scope(query->function_header->scope, &sln);
+
+poly_query_done:
+        solved_count += 1;
+    }
+
+    if (solved_count != bh_arr_length(query->proc->poly_params)) {
+        if (solved_something || query->successful_symres) {
+            return Check_Return_To_Symres;
+        } else {
+            return Check_Failed;
+        }
+    }
+
+    return Check_Complete;
+}
+
+void check_entity(Entity* ent) {
+    CheckStatus cs = Check_Success;
+
+    switch (ent->type) {
+        case Entity_Type_Foreign_Function_Header:
+        case Entity_Type_Function_Header:          cs = check_function_header(ent->function); break;
+        case Entity_Type_Temp_Function_Header:     cs = check_temp_function_header(ent->function); break;
+        case Entity_Type_Function:                 cs = check_function(ent->function); break;
+        case Entity_Type_Overloaded_Function:      cs = check_overloaded_function(ent->overloaded_function); break;
+        case Entity_Type_Global:                   cs = check_global(ent->global); break;
+        case Entity_Type_Struct_Member_Default:    cs = check_struct_defaults((AstStructType *) ent->type_alias); break;
+        case Entity_Type_Memory_Reservation_Type:  cs = check_memres_type(ent->mem_res); break;
+        case Entity_Type_Memory_Reservation:       cs = check_memres(ent->mem_res); break;
+        case Entity_Type_Static_If:                cs = check_static_if(ent->static_if); break;
+        case Entity_Type_Macro:                    cs = check_macro(ent->macro); break;
+        case Entity_Type_Constraint_Check:         cs = check_constraint(ent->constraint); break;
+        case Entity_Type_Polymorph_Query:          cs = check_polyquery(ent->poly_query); break;
+        case Entity_Type_Enum_Value:               cs = check_expression(&ent->enum_value->value); break;
+        case Entity_Type_Process_Directive:        cs = check_process_directive((AstNode *) ent->expr); break;
+
+        case Entity_Type_Expression:
+            cs = check_expression(&ent->expr);
+            resolve_expression_type(ent->expr);
+            break;
+
+        case Entity_Type_Type_Alias:
+            if (ent->type_alias->kind == Ast_Kind_Struct_Type)
+                cs = check_struct((AstStructType *) ent->type_alias);
+            else
+                cs = check_type(&ent->type_alias);
+            break;
+
+        case Entity_Type_File_Contents:
+            if (context.options->no_file_contents) {
+                onyx_report_error(ent->expr->token->pos, Error_Critical, "#file_contents is disabled for this compilation.");
+            }
+            break;
+
+        default: break;
+    }
+
+    switch (cs) {
+        case Check_Yield_Macro:      ent->macro_attempts++; break;
+        case Check_Success:          ent->state = Entity_State_Code_Gen;        goto clear_attempts;
+        case Check_Complete:         ent->state = Entity_State_Finalized;       goto clear_attempts;
+        case Check_Return_To_Symres: ent->state = Entity_State_Resolve_Symbols; goto clear_attempts;
+        case Check_Failed:           ent->state = Entity_State_Failed;          goto clear_attempts;
+
+    clear_attempts:
+        ent->macro_attempts = 0;
+        ent->micro_attempts = 0;
+    }
+}
diff --git a/compiler/src/clone.c b/compiler/src/clone.c
new file mode 100644 (file)
index 0000000..828f661
--- /dev/null
@@ -0,0 +1,643 @@
+#include "astnodes.h"
+#include "parser.h"
+#include "utils.h"
+
+// Weird flags that shouldn't be used too often because they complicate things
+static b32 dont_copy_structs = 0;
+
+static inline b32 should_clone(AstNode* node) {
+    if (node->flags & Ast_Flag_No_Clone) return 0;
+
+    if (dont_copy_structs) {
+        if (node->kind == Ast_Kind_Struct_Type) return 0;
+        if (node->kind == Ast_Kind_Function)    return 0;
+        if (node->kind == Ast_Kind_Polymorphic_Proc) return 0;
+    }
+
+    switch (node->kind) {
+        // List of nodes that should not be copied
+        case Ast_Kind_Global:
+        case Ast_Kind_Memres:
+        case Ast_Kind_StrLit:
+        case Ast_Kind_Package:
+        case Ast_Kind_Overloaded_Function:
+        case Ast_Kind_Alias:
+        case Ast_Kind_Code_Block:
+        case Ast_Kind_Macro:
+        case Ast_Kind_Symbol:
+        case Ast_Kind_Poly_Struct_Type:
+        case Ast_Kind_Basic_Type:
+        case Ast_Kind_Enum_Type:
+        case Ast_Kind_Enum_Value:
+            return 0;
+
+        default: return 1;
+    }
+}
+
+static inline i32 ast_kind_to_size(AstNode* node) {
+    switch (node->kind) {
+        case Ast_Kind_Error: return sizeof(AstNode);
+        case Ast_Kind_Package: return sizeof(AstPackage);
+        case Ast_Kind_Load_File: return sizeof(AstInclude);
+        case Ast_Kind_Load_Path: return sizeof(AstInclude);
+        case Ast_Kind_Load_All: return sizeof(AstInclude);
+        case Ast_Kind_Memres: return sizeof(AstMemRes);
+        case Ast_Kind_Binding: return sizeof(AstBinding);
+        case Ast_Kind_Function: return sizeof(AstFunction);
+        case Ast_Kind_Overloaded_Function: return sizeof(AstOverloadedFunction);
+        case Ast_Kind_Polymorphic_Proc: return sizeof(AstFunction);
+        case Ast_Kind_Block: return sizeof(AstBlock);
+        case Ast_Kind_Local: return sizeof(AstLocal);
+        case Ast_Kind_Global: return sizeof(AstGlobal);
+        case Ast_Kind_Symbol: return sizeof(AstNode);
+        case Ast_Kind_Unary_Op: return sizeof(AstUnaryOp);
+        case Ast_Kind_Binary_Op: return sizeof(AstBinaryOp);
+        case Ast_Kind_Type_Start: return 0;
+        case Ast_Kind_Type: return sizeof(AstType);
+        case Ast_Kind_Basic_Type: return sizeof(AstBasicType);
+        case Ast_Kind_Pointer_Type: return sizeof(AstPointerType);
+        case Ast_Kind_Function_Type: return sizeof(AstFunctionType) + ((AstFunctionType *) node)->param_count * sizeof(AstType *);
+        case Ast_Kind_Array_Type: return sizeof(AstArrayType);
+        case Ast_Kind_Slice_Type: return sizeof(AstSliceType);
+        case Ast_Kind_DynArr_Type: return sizeof(AstDynArrType);
+        case Ast_Kind_VarArg_Type: return sizeof(AstVarArgType);
+        case Ast_Kind_Struct_Type: return sizeof(AstStructType);
+        case Ast_Kind_Poly_Struct_Type: return sizeof(AstPolyStructType);
+        case Ast_Kind_Poly_Call_Type: return sizeof(AstPolyCallType);
+        case Ast_Kind_Enum_Type: return sizeof(AstEnumType);
+        case Ast_Kind_Type_Alias: return sizeof(AstTypeAlias);
+        case Ast_Kind_Type_Raw_Alias: return sizeof(AstTypeRawAlias);
+        case Ast_Kind_Type_Compound: return sizeof(AstCompoundType);
+        case Ast_Kind_Typeof: return sizeof(AstTypeOf);
+        case Ast_Kind_Type_End: return 0;
+        case Ast_Kind_Struct_Member: return sizeof(AstStructMember);
+        case Ast_Kind_Enum_Value: return sizeof(AstEnumValue);
+        case Ast_Kind_NumLit: return sizeof(AstNumLit);
+        case Ast_Kind_StrLit: return sizeof(AstStrLit);
+        case Ast_Kind_Param: return sizeof(AstLocal);
+        case Ast_Kind_Argument: return sizeof(AstArgument);
+        case Ast_Kind_Call: return sizeof(AstCall);
+        case Ast_Kind_Intrinsic_Call: return sizeof(AstCall);
+        case Ast_Kind_Return: return sizeof(AstReturn);
+        case Ast_Kind_Address_Of: return sizeof(AstAddressOf);
+        case Ast_Kind_Dereference: return sizeof(AstDereference);
+        case Ast_Kind_Subscript: return sizeof(AstSubscript);
+        case Ast_Kind_Slice: return sizeof(AstSubscript);
+        case Ast_Kind_Field_Access: return sizeof(AstFieldAccess);
+        case Ast_Kind_Unary_Field_Access: return sizeof(AstUnaryFieldAccess);
+        case Ast_Kind_Pipe: return sizeof(AstBinaryOp);
+        case Ast_Kind_Range_Literal: return sizeof(AstRangeLiteral);
+        case Ast_Kind_Method_Call: return sizeof(AstBinaryOp);
+        case Ast_Kind_Size_Of: return sizeof(AstSizeOf);
+        case Ast_Kind_Align_Of: return sizeof(AstAlignOf);
+        case Ast_Kind_File_Contents: return sizeof(AstFileContents);
+        case Ast_Kind_Struct_Literal: return sizeof(AstStructLiteral);
+        case Ast_Kind_Array_Literal: return sizeof(AstArrayLiteral);
+        case Ast_Kind_If: return sizeof(AstIfWhile);
+        case Ast_Kind_For: return sizeof(AstFor);
+        case Ast_Kind_While: return sizeof(AstIfWhile);
+        case Ast_Kind_Jump: return sizeof(AstJump);
+        case Ast_Kind_Use: return sizeof(AstUse);
+        case Ast_Kind_Defer: return sizeof(AstDefer);
+        case Ast_Kind_Switch: return sizeof(AstSwitch);
+        case Ast_Kind_Switch_Case: return sizeof(AstSwitchCase);
+        case Ast_Kind_Directive_Solidify: return sizeof(AstDirectiveSolidify);
+        case Ast_Kind_Compound: return sizeof(AstCompound);
+        case Ast_Kind_Named_Value: return sizeof(AstNamedValue);
+        case Ast_Kind_Call_Site: return sizeof(AstCallSite);
+        case Ast_Kind_Static_If: return sizeof(AstIfWhile);
+        case Ast_Kind_If_Expression: return sizeof(AstIfExpression);
+        case Ast_Kind_Directive_Insert: return sizeof(AstDirectiveInsert);
+        case Ast_Kind_Directive_Defined: return sizeof(AstDirectiveDefined);
+        case Ast_Kind_Do_Block: return sizeof(AstDoBlock);
+        case Ast_Kind_Constraint: return sizeof(AstConstraint);
+        case Ast_Kind_Directive_Remove: return sizeof(AstDirectiveRemove);
+        case Ast_Kind_Count: return 0;
+    }
+
+    return 0;
+}
+
+AstNode* ast_clone_list(bh_allocator a, void* n) {
+    AstNode* node = (AstNode *) n;
+    if (node == NULL) return NULL;
+
+    AstNode* root = ast_clone(a, node);
+    AstNode* curr = root->next;
+    AstNode** insertion = &root->next;
+
+    while (curr != NULL) {
+        curr = ast_clone(a, curr);
+        *insertion = curr;
+        insertion = &curr->next;
+        curr = curr->next;
+    }
+
+    return root;
+}
+
+static bh_arr(AstNode *) captured_entities=NULL;
+#define E(ent) do { \
+    assert(captured_entities); \
+    ent->entity = NULL; \
+    bh_arr_push(captured_entities, (AstNode *) ent); \
+    } while (0);
+    
+
+#define C(nt, mname) ((nt *) nn)->mname = (void *) ast_clone(a, ((nt *) node)->mname);
+
+// NOTE: Using void* to avoid a lot of unnecessary casting
+AstNode* ast_clone(bh_allocator a, void* n) {
+    AstNode* node = (AstNode *) n;
+
+    if (node == NULL) return NULL;
+    if (!should_clone(node)) return node;
+
+    static int clone_depth = 0;
+    clone_depth++;
+
+    i32 node_size = ast_kind_to_size(node);
+    // bh_printf("Cloning %s with size %d\n", onyx_ast_node_kind_string(node->kind), node_size);
+
+    AstNode* nn = onyx_ast_node_new(a, node_size, node->kind);
+    memmove(nn, node, node_size);
+
+    switch ((u16) node->kind) {
+        case Ast_Kind_Binary_Op:
+        case Ast_Kind_Pipe:
+        case Ast_Kind_Method_Call:
+            C(AstBinaryOp, left);
+            C(AstBinaryOp, right);
+            break;
+
+        case Ast_Kind_Unary_Op:
+            C(AstUnaryOp, expr);
+            C(AstUnaryOp, type_node);
+            break;
+
+        case Ast_Kind_Param:
+        case Ast_Kind_Local:
+            C(AstLocal, type_node);
+            break;
+
+        case Ast_Kind_Call:
+            C(AstCall, callee);
+            arguments_deep_clone(a, &((AstCall *) nn)->args, &((AstCall *) node)->args);
+            break;
+
+        case Ast_Kind_Argument:
+            C(AstArgument, value);
+            break;
+
+        case Ast_Kind_Address_Of:
+            C(AstAddressOf, expr);
+            break;
+
+        case Ast_Kind_Dereference:
+            C(AstDereference, expr);
+            break;
+
+        case Ast_Kind_Slice:
+        case Ast_Kind_Subscript:
+            C(AstSubscript, addr);
+            C(AstSubscript, expr);
+            break;
+
+        case Ast_Kind_Field_Access:
+            C(AstFieldAccess, expr);
+            break;
+
+        case Ast_Kind_Size_Of:
+            C(AstSizeOf, so_ast_type);
+            break;
+
+        case Ast_Kind_Align_Of:
+            C(AstAlignOf, ao_ast_type);
+            break;
+
+        case Ast_Kind_Struct_Literal: {
+            AstStructLiteral* st = (AstStructLiteral *) node;
+            AstStructLiteral* dt = (AstStructLiteral *) nn;
+
+            dt->stnode = (AstTyped *) ast_clone(a, st->stnode);
+
+            arguments_deep_clone(a, &dt->args, &st->args);
+            break;
+        }
+
+        case Ast_Kind_Array_Literal: {
+            AstArrayLiteral* st = (AstArrayLiteral *) node;
+            AstArrayLiteral* dt = (AstArrayLiteral *) nn;
+
+            dt->atnode = (AstTyped *) ast_clone(a, st->atnode);
+
+            dt->values = NULL;
+            bh_arr_new(global_heap_allocator, dt->values, bh_arr_length(st->values));
+            bh_arr_each(AstTyped *, val, st->values)
+                bh_arr_push(dt->values, (AstTyped *) ast_clone(a, *val));
+
+            break;
+        }
+
+        case Ast_Kind_Range_Literal:
+            C(AstRangeLiteral, low);
+            C(AstRangeLiteral, high);
+            C(AstRangeLiteral, step);
+            break;
+
+        case Ast_Kind_Return:
+            C(AstReturn, expr);
+            break;
+
+        case Ast_Kind_Block:
+            ((AstBlock *) nn)->body = ast_clone_list(a, ((AstBlock *) node)->body);
+            break;
+
+        case Ast_Kind_Defer:
+            C(AstDefer, stmt);
+            break;
+
+        case Ast_Kind_For:
+            C(AstFor, var);
+            C(AstFor, iter);
+            C(AstFor, stmt);
+            break;
+
+        case Ast_Kind_If:
+        case Ast_Kind_While:
+            ((AstIfWhile *) nn)->initialization = ast_clone_list(a, ((AstIfWhile *) node)->initialization);
+            //fallthrough
+
+        case Ast_Kind_Static_If:
+            C(AstIfWhile, cond);
+
+            C(AstIfWhile, true_stmt);
+            C(AstIfWhile, false_stmt);
+
+            if (nn->kind == Ast_Kind_Static_If) {
+                ((AstIfWhile *) node)->flags |= Ast_Flag_Dead;
+                ((AstIfWhile *) node)->flags |= Ast_Flag_Static_If_Resolved;
+
+                ((AstIfWhile *) nn)->flags &= ~Ast_Flag_Dead;
+                ((AstIfWhile *) nn)->flags &= ~Ast_Flag_Static_If_Resolved;
+                E(nn);
+            }
+
+            break;
+
+        case Ast_Kind_Switch_Case: {
+            C(AstSwitchCase, block);
+
+            AstSwitchCase *dw = (AstSwitchCase *) nn;
+            AstSwitchCase *sw = (AstSwitchCase *) node;
+
+            dw->values = NULL;
+            bh_arr_new(global_heap_allocator, dw->values, bh_arr_length(sw->values));
+            bh_arr_each(AstTyped *, value, sw->values)
+                bh_arr_push(dw->values, (AstTyped *) ast_clone(a, *value));
+
+            break;
+        }
+
+        case Ast_Kind_Switch: {
+            AstSwitch* dw = (AstSwitch *) nn;
+            AstSwitch* sw = (AstSwitch *) node;
+
+            dw->initialization = ast_clone_list(a, sw->initialization);
+            C(AstSwitch, expr);
+
+
+            dw->cases = NULL;
+            C(AstSwitch, case_block);
+            break;
+        }
+
+        case Ast_Kind_Pointer_Type:
+            C(AstPointerType, elem);
+            break;
+
+        case Ast_Kind_Array_Type:
+            C(AstArrayType, count_expr);
+            C(AstArrayType, elem);
+            break;
+
+        case Ast_Kind_Slice_Type:
+            C(AstSliceType, elem);
+            break;
+
+        case Ast_Kind_DynArr_Type:
+            C(AstDynArrType, elem);
+            break;
+
+        case Ast_Kind_VarArg_Type:
+            C(AstVarArgType, elem);
+            break;
+
+        case Ast_Kind_Type_Alias:
+            C(AstTypeAlias, to);
+            break;
+
+        case Ast_Kind_Struct_Type: {
+            AstStructType* ds = (AstStructType *) nn;
+            AstStructType* ss = (AstStructType *) node;
+
+            ds->members = NULL;
+            bh_arr_new(global_heap_allocator, ds->members, bh_arr_length(ss->members));
+
+            bh_arr_each(AstStructMember *, smem, ss->members) {
+                bh_arr_push(ds->members, (AstStructMember *) ast_clone(a, *smem));
+            }
+
+            ds->meta_tags = NULL;
+            bh_arr_new(global_heap_allocator, ds->meta_tags, bh_arr_length(ss->meta_tags));
+            bh_arr_each(AstTyped *, tag, ss->meta_tags) {
+                bh_arr_push(ds->meta_tags, (AstTyped *) ast_clone(a, *tag));
+            }
+            
+            if (ss->constraints.constraints) {
+                memset(&ds->constraints, 0, sizeof(ConstraintContext));
+                bh_arr_new(global_heap_allocator, ds->constraints.constraints, bh_arr_length(ss->constraints.constraints));
+
+                bh_arr_each(AstConstraint *, constraint, ss->constraints.constraints) {
+                    bh_arr_push(ds->constraints.constraints, (AstConstraint *) ast_clone(a, (AstNode *) *constraint));
+                }
+            }
+
+            ds->stcache = NULL;
+            break;
+        }
+
+        case Ast_Kind_Struct_Member: {
+            C(AstStructMember, type_node);
+            C(AstStructMember, initial_value);
+
+            AstStructMember *ds = (AstStructMember *) nn;
+            AstStructMember *ss = (AstStructMember *) node;
+
+            ds->meta_tags = NULL;
+            bh_arr_new(global_heap_allocator, ds->meta_tags, bh_arr_length(ss->meta_tags));
+            bh_arr_each(AstTyped *, tag, ss->meta_tags) {
+                bh_arr_push(ds->meta_tags, (AstTyped *) ast_clone(a, *tag));
+            }
+
+            break;
+        }
+
+        case Ast_Kind_Poly_Call_Type: {
+            AstPolyCallType* pcd = (AstPolyCallType *) nn;
+            AstPolyCallType* pcs = (AstPolyCallType *) node;
+
+            pcd->callee = (AstType *) ast_clone(a, pcs->callee);
+            pcd->params = NULL;
+            bh_arr_new(global_heap_allocator, pcd->params, bh_arr_length(pcs->params));
+
+            bh_arr_each(AstNode *, param, pcs->params) {
+                bh_arr_push(pcd->params, ast_clone(a, *param));
+            }
+
+            break;
+        }
+
+        case Ast_Kind_Type_Compound: {
+            AstCompoundType* cd = (AstCompoundType *) nn;
+            AstCompoundType* cs = (AstCompoundType *) node;
+
+            cd->types = NULL;
+            bh_arr_new(global_heap_allocator, cd->types, bh_arr_length(cs->types));
+
+            bh_arr_each(AstType *, type, cs->types) {
+                bh_arr_push(cd->types, (AstType *) ast_clone(a, (AstNode *) *type));
+            }
+            break;
+        }
+
+        case Ast_Kind_Function_Type:
+            C(AstFunctionType, return_type);
+            ((AstFunctionType *) nn)->param_count = ((AstFunctionType *) node)->param_count;
+            fori (i, 0, (i64) ((AstFunctionType *) nn)->param_count) {
+                ((AstFunctionType *) nn)->params[i] = (AstType *) ast_clone(a, ((AstFunctionType *) node)->params[i]);
+            }
+            break;
+
+        case Ast_Kind_Binding:
+            bh_printf("Cloning binding: %b\n", node->token->text, node->token->length);
+            C(AstTyped, type_node);
+            C(AstBinding, node);
+            break;
+
+        case Ast_Kind_Function:
+        case Ast_Kind_Polymorphic_Proc: {
+            if (clone_depth > 1) {
+                clone_depth--;
+                return node;
+            }
+
+            AstFunction* df = (AstFunction *) nn;
+            AstFunction* sf = (AstFunction *) node;
+
+            convert_polyproc_to_function(df);
+
+            if (sf->is_foreign) return node;
+            assert(df->scope == NULL);
+
+            df->nodes_that_need_entities_after_clone = NULL;
+            bh_arr_new(global_heap_allocator, df->nodes_that_need_entities_after_clone, 1);
+            captured_entities = df->nodes_that_need_entities_after_clone;
+
+            df->return_type = (AstType *) ast_clone(a, sf->return_type);
+            df->body = (AstBlock *) ast_clone(a, sf->body);
+
+            df->nodes_that_need_entities_after_clone = captured_entities;
+            captured_entities = NULL;
+
+            df->params = NULL;
+            bh_arr_new(context.ast_alloc, df->params, bh_arr_length(sf->params));
+
+            bh_arr_each(AstParam, param, sf->params) {
+                AstParam new_param = { 0 };
+
+                dont_copy_structs = 1;
+                new_param.local = (AstLocal *) ast_clone(a, param->local);
+                new_param.local->flags &= ~Ast_Flag_Param_Symbol_Dirty;
+                new_param.default_value = (AstTyped *) ast_clone(a, param->default_value);
+                new_param.use_processed = 0;
+                dont_copy_structs = 0;
+
+                new_param.vararg_kind = param->vararg_kind;
+                new_param.is_used = param->is_used;
+                bh_arr_push(df->params, new_param);
+            }
+
+            if (sf->constraints.constraints) {
+                memset(&df->constraints, 0, sizeof(ConstraintContext));
+                bh_arr_new(context.ast_alloc, df->constraints.constraints, bh_arr_length(sf->constraints.constraints));
+
+                bh_arr_each(AstConstraint *, constraint, sf->constraints.constraints) {
+                    bh_arr_push(df->constraints.constraints, (AstConstraint *) ast_clone(a, (AstNode *) *constraint));
+                }
+            }
+
+            if (sf->tags) {
+                bh_arr_new(context.ast_alloc, df->tags, bh_arr_length(sf->tags));
+                bh_arr_each(AstTyped *, pexpr, sf->tags) {
+                    bh_arr_push(df->tags, (AstTyped *) ast_clone(a, (AstNode *) *pexpr));
+                }    
+            }
+
+            break;
+        }
+
+        case Ast_Kind_Constraint: {
+            C(AstConstraint, interface);
+
+            AstConstraint* dc = (AstConstraint *) nn;
+            AstConstraint* sc = (AstConstraint *) node;
+
+            dc->type_args = NULL;
+            bh_arr_new(global_heap_allocator, dc->type_args, bh_arr_length(sc->type_args));
+
+            bh_arr_each(AstType *, type_arg, sc->type_args) {
+                bh_arr_push(dc->type_args, (AstType *) ast_clone(a, (AstNode *) *type_arg));
+            }
+
+            dc->phase = Constraint_Phase_Waiting_To_Be_Queued;
+            break;
+        }
+
+        case Ast_Kind_Use:
+            C(AstUse, expr);
+            break;
+
+        case Ast_Kind_Directive_Solidify: {
+            AstDirectiveSolidify* dd = (AstDirectiveSolidify *) nn;
+            AstDirectiveSolidify* sd = (AstDirectiveSolidify *) node;
+
+            dd->poly_proc = (AstFunction *) ast_clone(a, (AstNode *) sd->poly_proc);
+            dd->resolved_proc = NULL;
+
+            dd->known_polyvars = NULL;
+            bh_arr_new(global_heap_allocator, dd->known_polyvars, bh_arr_length(sd->known_polyvars));
+
+            bh_arr_each(AstPolySolution, sln, sd->known_polyvars) {
+                AstPolySolution new_sln;
+                new_sln.kind     = sln->kind;
+                new_sln.poly_sym = (AstNode *) ast_clone(a, (AstNode *) sln->poly_sym);
+                new_sln.ast_type = (AstType *) ast_clone(a, (AstNode *) sln->ast_type);
+                bh_arr_push(dd->known_polyvars, new_sln);
+            }
+
+            break;
+        }
+
+        case Ast_Kind_Compound: {
+            AstCompound* cd = (AstCompound *) nn;
+            AstCompound* cs = (AstCompound *) node;
+
+            cd->exprs = NULL;
+            bh_arr_new(global_heap_allocator, cd->exprs, bh_arr_length(cs->exprs));
+
+            bh_arr_each(AstTyped *, expr, cs->exprs) {
+                bh_arr_push(cd->exprs, (AstTyped *) ast_clone(a, (AstNode *) *expr));
+            }
+            break;
+        }
+
+        case Ast_Kind_Named_Value:
+            C(AstNamedValue, value);
+            break;
+
+        case Ast_Kind_If_Expression:
+            C(AstIfExpression, cond);
+            C(AstIfExpression, true_expr);
+            C(AstIfExpression, false_expr);
+            break;
+
+        case Ast_Kind_Directive_Insert:
+            C(AstDirectiveInsert, code_expr);
+            break;
+
+        case Ast_Kind_Directive_Defined:
+            C(AstDirectiveDefined, expr);
+            ((AstDirectiveDefined *) nn)->is_defined = 0;
+            break;
+
+        case Ast_Kind_Typeof:
+            C(AstTypeOf, expr);
+            ((AstTypeOf *) nn)->resolved_type = NULL;
+            break;
+
+        case Ast_Kind_Do_Block:
+            C(AstDoBlock, block);
+            ((AstDoBlock *) nn)->type_node = (AstType *) &basic_type_auto_return;
+            break;
+
+        case Ast_Kind_File_Contents:
+            C(AstFileContents, filename_expr);
+            E(nn);
+            break;
+    }
+
+    clone_depth--;
+    return nn;
+}
+
+#undef C
+
+AstFunction* clone_function_header(bh_allocator a, AstFunction* func) {
+    if (func->kind != Ast_Kind_Function && func->kind != Ast_Kind_Polymorphic_Proc) return NULL;
+
+    if (func->is_foreign) return func;
+
+    AstFunction* new_func = onyx_ast_node_new(a, sizeof(AstFunction), func->kind);
+    memmove(new_func, func, sizeof(AstFunction));
+    assert(new_func->scope == NULL);
+
+    convert_polyproc_to_function(new_func);
+
+    new_func->return_type = (AstType *) ast_clone(a, func->return_type);
+
+    new_func->params = NULL;
+    bh_arr_new(global_heap_allocator, new_func->params, bh_arr_length(func->params));
+    bh_arr_each(AstParam, param, func->params) {
+        AstParam new_param;
+
+        dont_copy_structs = 1;
+        new_param.local = (AstLocal *) ast_clone(a, param->local);
+        new_param.local->flags &= ~Ast_Flag_Param_Symbol_Dirty;
+        new_param.default_value = (AstTyped *) ast_clone(a, param->default_value);
+        new_param.use_processed = 0;
+        dont_copy_structs = 0;
+
+        new_param.vararg_kind = param->vararg_kind;
+        new_param.is_used = param->is_used;
+        bh_arr_push(new_func->params, new_param);
+    }
+
+    if (func->constraints.constraints) {
+        memset(&new_func->constraints, 0, sizeof(ConstraintContext));
+        bh_arr_new(global_heap_allocator, new_func->constraints.constraints, bh_arr_length(func->constraints.constraints));
+
+        bh_arr_each(AstConstraint *, constraint, func->constraints.constraints) {
+            bh_arr_push(new_func->constraints.constraints, (AstConstraint *) ast_clone(a, (AstNode *) *constraint));
+        }
+    }
+
+    return new_func;
+}
+
+// Clones a function body from a given function. It is assumed that `dest` is
+// a function from `clone_function_header`.
+void clone_function_body(bh_allocator a, AstFunction* dest, AstFunction* source) {
+    if (dest->kind != Ast_Kind_Function) return;
+    if (source->kind != Ast_Kind_Polymorphic_Proc && source->kind != Ast_Kind_Function) return;
+
+    dest->nodes_that_need_entities_after_clone = NULL;
+    bh_arr_new(global_heap_allocator, dest->nodes_that_need_entities_after_clone, 1);
+    captured_entities = dest->nodes_that_need_entities_after_clone;
+
+    dest->body = (AstBlock *) ast_clone(a, source->body);
+    
+    dest->nodes_that_need_entities_after_clone = captured_entities;
+    captured_entities = NULL;
+}
diff --git a/compiler/src/doc.c b/compiler/src/doc.c
new file mode 100644 (file)
index 0000000..2a8bbd4
--- /dev/null
@@ -0,0 +1,292 @@
+#include "doc.h"
+#include "utils.h"
+#include "types.h"
+
+static i32 cmp_doc_entry(const void * a, const void * b) {
+    DocEntry* d1 = (DocEntry *) a;
+    DocEntry* d2 = (DocEntry *) b;
+
+    return strncmp(d1->def, d2->def, 1024);
+}
+
+static i32 cmp_doc_package(const void * a, const void * b) {
+    DocPackage* d1 = (DocPackage *) a;
+    DocPackage* d2 = (DocPackage *) b;
+
+    return strncmp(d1->name, d2->name, 1024);
+}
+
+static char* node_to_doc_def(const char* sym, AstNode *node, bh_allocator a) {
+    static char buf[1024];
+    memset(buf, 0, 1023);
+
+    strncat(buf, sym, 1023);
+    strncat(buf, " :: ", 1023);
+
+    switch (node->kind) {
+        case Ast_Kind_Function: {
+            strncat(buf, "(", 1023);
+
+            AstFunction *func = (AstFunction *) node;
+            bh_arr_each(AstParam, param, func->params) {
+                if (param != func->params)
+                    strncat(buf, ", ", 1023);
+
+                token_toggle_end(param->local->token);
+                strncat(buf, param->local->token->text, 1023);
+                token_toggle_end(param->local->token);
+
+                strncat(buf, ": ", 1023);
+
+                strncat(buf, type_get_name(param->local->type), 1023);
+
+                if (param->default_value != NULL) {
+                    strncat(buf, " = <default>", 1023);
+                }
+            }
+
+            strncat(buf, ") -> ", 1023);
+            strncat(buf, type_get_name(func->type->Function.return_type), 1023);
+
+            break;
+        }
+
+        case Ast_Kind_Struct_Type: {
+            strncat(buf, "struct { ", 1023);
+
+            AstStructType* st = (AstStructType *) node;
+            bh_arr_each(AstStructMember *, smem, st->members) {
+
+                token_toggle_end((*smem)->token);
+                strncat(buf, (*smem)->token->text, 1023);
+                token_toggle_end((*smem)->token);
+
+                strncat(buf, ": ", 1023);
+
+                strncat(buf, type_get_name((*smem)->type), 1023);
+
+                strncat(buf, "; ", 1023);
+            }
+
+            strncat(buf, "}", 1023);
+            break;
+        }
+
+        case Ast_Kind_Basic_Type:
+        case Ast_Kind_Array_Type:
+        case Ast_Kind_Type_Alias:
+        case Ast_Kind_Slice_Type:
+        case Ast_Kind_DynArr_Type: {
+            strncat(buf, type_get_name(type_build_from_ast(global_heap_allocator, (AstType *) node)), 1023);
+            break;
+        }
+
+        default: {
+            strncat(buf, "<unimplemented printing>", 1023);
+        }
+    }
+
+    return bh_strdup(a, buf);
+}
+
+static DocPackage doc_package_create(Package* p, bh_allocator a) {
+    DocPackage dp;
+    dp.name = p->name;
+    dp.public_entries = NULL;
+    dp.private_entries = NULL;
+
+    bh_arr_new(global_heap_allocator, dp.public_entries, 16);
+    bh_arr_new(global_heap_allocator, dp.private_entries, 16);
+
+    fori (i, 0, shlen(p->scope->symbols)) {
+        char *key = p->scope->symbols[i].key;
+        AstNode *value = p->scope->symbols[i].value;
+
+        DocEntry de;
+        if (value->token) de.pos = value->token->pos;
+        de.def = node_to_doc_def(key, value, a);
+        de.sym = (char *) key;
+        de.additional = NULL;
+
+        bh_arr_push(dp.public_entries, de);
+    }
+
+    fori (i, 0, shlen(p->private_scope->symbols)) {
+        char *key = p->scope->symbols[i].key;
+        AstNode *value = p->scope->symbols[i].value;
+
+        DocEntry de;
+        if (value->token) de.pos = value->token->pos;
+        de.def = node_to_doc_def(key, value, a);
+        de.sym = (char *) key;
+        de.additional = NULL;
+
+        bh_arr_push(dp.private_entries, de);
+    }
+
+    qsort(dp.public_entries,  bh_arr_length(dp.public_entries),  sizeof(DocEntry), cmp_doc_entry);
+    qsort(dp.private_entries, bh_arr_length(dp.private_entries), sizeof(DocEntry), cmp_doc_entry);
+
+    return dp;
+}
+
+OnyxDocumentation onyx_docs_generate() {
+    OnyxDocumentation doc;
+
+    bh_arena_init(&doc.doc_arena, global_heap_allocator, 16 * 1024);
+    bh_allocator a = bh_arena_allocator(&doc.doc_arena);
+
+    doc.package_docs = NULL;
+    bh_arr_new(global_heap_allocator, doc.package_docs, 16);
+
+    fori (i, 0, shlen(context.packages)) {
+        DocPackage dp = doc_package_create(context.packages[i].value, a);
+        bh_arr_push(doc.package_docs, dp);
+    }
+
+    qsort(doc.package_docs, bh_arr_length(doc.package_docs), sizeof(DocPackage), cmp_doc_package);
+
+    return doc;
+}
+
+static void onyx_docs_emit_human(OnyxDocumentation* doc, bh_file* file) {
+    // NOTE: Disabling fancy line printing until I can make it better
+    #if 0
+    bh_arr_each(DocPackage, dp, doc->package_docs) {
+        bh_printf("Package '%s'\n", dp->name);
+
+        if (bh_arr_length(dp->public_entries) > 0) {
+            bh_printf("\xe2\x94\x9c\xe2\x94\x80 Public symbols\n");
+            bh_arr_each(DocEntry, de, dp->public_entries) {
+                bh_printf("\xe2\x94\x82  \xe2\x94\x9c\xe2\x94\x80 %s\n", de->def);
+
+                if (de->pos.filename != NULL)
+                    bh_printf("\xe2\x94\x82  \xe2\x94\x82    at %s:%d,%d\n", de->pos.filename, de->pos.line, de->pos.column);
+                else
+                    bh_printf("\xe2\x94\x82  \xe2\x94\x82    compiler built-in\n");
+
+                bh_printf("\xe2\x94\x82  \xe2\x94\x82\n");
+            }
+        }
+
+        if (bh_arr_length(dp->private_entries) > 0) {
+            bh_printf("\xe2\x94\x9c\xe2\x94\x80 Private symbols\n");
+            bh_arr_each(DocEntry, de, dp->private_entries) {
+                bh_printf("\xe2\x94\x82  \xe2\x94\x9c\xe2\x94\x80 %s\n", de->def);
+                if (de->pos.filename != NULL)
+                    bh_printf("\xe2\x94\x82  \xe2\x94\x82    at %s:%d,%d\n", de->pos.filename, de->pos.line, de->pos.column);
+                else
+                    bh_printf("\xe2\x94\x82  \xe2\x94\x82    compiler built-in\n");
+
+                bh_printf("\xe2\x94\x82  \xe2\x94\x82\n");
+            }
+
+            bh_printf("\n");
+        }
+    }
+    #else
+    bh_arr_each(DocPackage, dp, doc->package_docs) {
+        bh_fprintf(file, "Package '%s'\n", dp->name);
+
+        if (bh_arr_length(dp->public_entries) > 0) {
+            bh_fprintf(file, "   Public symbols\n");
+            bh_arr_each(DocEntry, de, dp->public_entries) {
+                bh_fprintf(file, "     %s\n", de->def);
+
+                if (de->pos.filename != NULL)
+                    bh_fprintf(file, "        at %s:%d,%d\n", de->pos.filename, de->pos.line, de->pos.column);
+                else
+                    bh_fprintf(file, "        compiler built-in\n");
+
+                bh_fprintf(file, "    \n");
+            }
+        }
+
+        if (bh_arr_length(dp->private_entries) > 0) {
+            bh_fprintf(file, "   Private symbols\n");
+            bh_arr_each(DocEntry, de, dp->private_entries) {
+                bh_fprintf(file, "      %s\n", de->def);
+                if (de->pos.filename != NULL)
+                    bh_fprintf(file, "        at %s:%d,%d\n", de->pos.filename, de->pos.line, de->pos.column);
+                else
+                    bh_fprintf(file, "        compiler built-in\n");
+
+                bh_fprintf(file, "    \n");
+            }
+
+            bh_fprintf(file, "\n");
+        }
+    }
+    #endif
+}
+
+
+static i32 sort_tags(const void* a, const void* b) {
+    DocEntry *da = *((DocEntry **) a);
+    DocEntry *db = *((DocEntry **) b);
+
+    return strcmp(da->sym, db->sym);
+}
+
+static void onyx_docs_emit_tags(OnyxDocumentation* doc) {
+    bh_file tags_file;
+    if (bh_file_create(&tags_file, "./tags") != BH_FILE_ERROR_NONE) {
+        bh_printf("Cannot create 'tags'.\n");
+        return;
+    }
+
+    bh_fprintf(&tags_file, "!_TAG_FILE_FORMAT\t2\n");
+    bh_fprintf(&tags_file, "!_TAG_FILE_SORTED\t1\n");
+    bh_fprintf(&tags_file, "!_TAG_OUTPUT_FILESEP\tslash\n");
+    bh_fprintf(&tags_file, "!_TAG_OUTPUT_MODE\tu-ctags\n");
+    bh_fprintf(&tags_file, "!_TAG_PROGRAM_AUTHOR\tOnyx Compiler\n");
+    bh_fprintf(&tags_file, "!_TAG_PROGRAM_NAME\tOnyx Compiler\n");
+    bh_fprintf(&tags_file, "!_TAG_PROGRAM_URL\thttps://github.com/brendanfh/onyx\n");
+    bh_fprintf(&tags_file, "!_TAG_PROGRAM_VERSION\t0.0.1\n");
+
+    bh_arr(DocEntry *) tags = NULL;
+    bh_arr_new(global_heap_allocator, tags, 256);
+
+    bh_arr_each(DocPackage, dp, doc->package_docs) {
+        bh_arr_each(DocEntry, de, dp->public_entries) bh_arr_push(tags, de);
+        bh_arr_each(DocEntry, de, dp->private_entries) bh_arr_push(tags, de);
+    }
+
+    qsort(tags, bh_arr_length(tags), sizeof(DocEntry *), sort_tags);
+
+    bh_arr_each(DocEntry *, tag, tags) {
+        if ((*tag)->pos.filename == NULL) continue;
+
+        i32 line_len = 0;
+        char *c = (*tag)->pos.line_start;
+        while (*c++ != '\n') line_len++;
+
+        bh_fprintf(&tags_file, "%s\t%s\t/^%b$/\n",
+                (*tag)->sym, (*tag)->pos.filename,
+                (*tag)->pos.line_start, line_len);
+    }
+
+    bh_file_close(&tags_file);
+}
+
+static void onyx_docs_emit_html(OnyxDocumentation* doc, bh_file* file) {
+    bh_fprintf(file, "HTML documentation output not supported yet.\n");
+    return;
+}
+
+void onyx_docs_emit(OnyxDocumentation* doc, const char* filename) {
+    bh_file file;
+    if (bh_file_create(&file, filename) != BH_FILE_ERROR_NONE) {
+        bh_printf("ERROR: Failed to open file '%s' for writing documentation.\n", filename);
+        return;
+    }
+
+    switch (doc->format) {
+        case Doc_Format_Human: onyx_docs_emit_human(doc, &file); break;
+        case Doc_Format_Html: onyx_docs_emit_html(doc, &file); break;
+        case Doc_Format_Tags: onyx_docs_emit_tags(doc); break;
+    }
+
+    bh_file_close(&file);
+}
+
diff --git a/compiler/src/entities.c b/compiler/src/entities.c
new file mode 100644 (file)
index 0000000..0ceb2f9
--- /dev/null
@@ -0,0 +1,406 @@
+#include "bh.h"
+#include "astnodes.h"
+#include "utils.h"
+
+static inline i32 entity_phase(Entity* e1) {
+    if (e1->state <= Entity_State_Parse && e1->macro_attempts == 0) return 1;
+    if (e1->state <  Entity_State_Code_Gen) return 2;
+    return 3;
+}
+
+// NOTE: Returns >0 if e1 should be processed after e2.
+static i32 entity_compare(Entity* e1, Entity* e2) {
+    i32 phase1 = entity_phase(e1);
+    i32 phase2 = entity_phase(e2);
+
+    if (phase1 != phase2)
+        return phase1 - phase2;
+    else if (e1->macro_attempts != e2->macro_attempts)
+        return (i32) e1->macro_attempts - (i32) e2->macro_attempts;
+    else if (e1->state != e2->state)
+        return (i32) e1->state - (i32) e2->state;
+    else if (e1->type != e2->type)
+        return (i32) e1->type - (i32) e2->type;
+    else if (e1->micro_attempts != e2->micro_attempts)
+        return (i32) (e1->micro_attempts - e2->micro_attempts);
+    else
+        return (i32) (e1->id - e2->id);
+}
+
+#define eh_parent(index) (((index) - 1) / 2)
+#define eh_lchild(index) (((index) * 2) + 1)
+#define eh_rchild(index) (((index) * 2) + 2)
+
+static void eh_shift_up(EntityHeap* entities, i32 index) {
+    while (index > 0 && entity_compare(entities->entities[eh_parent(index)], entities->entities[index]) > 0) {
+        Entity* tmp = entities->entities[eh_parent(index)];
+        entities->entities[eh_parent(index)] = entities->entities[index];
+        entities->entities[index] = tmp;
+
+        index = eh_parent(index);
+    }
+}
+
+static void eh_shift_down(EntityHeap* entities, i32 index) {
+    while (1) {
+        i32 min_index = index;
+
+        i32 l = eh_lchild(index);
+        if (l < bh_arr_length(entities->entities)
+            && entity_compare(entities->entities[l], entities->entities[min_index]) < 0) {
+            min_index = l;
+        }
+
+        i32 r = eh_rchild(index);
+        if (r < bh_arr_length(entities->entities)
+            && entity_compare(entities->entities[r], entities->entities[min_index]) < 0) {
+            min_index = r;
+        }
+
+        if (index != min_index) {
+            Entity* tmp = entities->entities[min_index];
+            entities->entities[min_index] = entities->entities[index];
+            entities->entities[index] = tmp;
+
+            index = min_index;
+            continue;
+        }
+
+        break;
+    }
+}
+
+void entity_heap_init(EntityHeap* entities) {
+    bh_arena_init(&entities->entity_arena, global_heap_allocator, 32 * 1024);
+}
+
+static u32 next_entity_id = 0;
+
+// Allocates the entity in the entity heap. Don't quite feel this is necessary...
+Entity* entity_heap_register(EntityHeap* entities, Entity e) {
+    bh_allocator alloc = bh_arena_allocator(&entities->entity_arena);
+    Entity* entity = bh_alloc_item(alloc, Entity);
+    *entity = e;
+    entity->id = next_entity_id++;
+    entity->macro_attempts = 0;
+    entity->micro_attempts = 0;
+    entity->entered_in_queue = 0;
+
+    return entity;
+}
+
+void entity_heap_insert_existing(EntityHeap* entities, Entity* e) {
+    if (e->entered_in_queue) return;
+
+    if (entities->entities == NULL) {
+        bh_arr_new(global_heap_allocator, entities->entities, 128);
+    }
+
+    bh_arr_push(entities->entities, e);
+    eh_shift_up(entities, bh_arr_length(entities->entities) - 1);
+    e->entered_in_queue = 1;
+
+    entities->state_count[e->state]++;
+    entities->type_count[e->type]++;
+    entities->all_count[e->state][e->type]++;
+}
+
+Entity* entity_heap_insert(EntityHeap* entities, Entity e) {
+    Entity* entity = entity_heap_register(entities, e);
+    entity_heap_insert_existing(entities, entity);
+    return entity;
+}
+
+Entity* entity_heap_top(EntityHeap* entities) {
+    return entities->entities[0];
+}
+
+void entity_heap_change_top(EntityHeap* entities, Entity* new_top) {
+    entities->state_count[entities->entities[0]->state]--;
+    entities->state_count[new_top->state]++;
+
+    entities->type_count[entities->entities[0]->type]--;
+    entities->type_count[new_top->type]++;
+
+    entities->all_count[entities->entities[0]->state][entities->entities[0]->type]--;
+    entities->all_count[new_top->state][new_top->type]++;
+
+    entities->entities[0] = new_top;
+    eh_shift_down(entities, 0);
+}
+
+void entity_heap_remove_top(EntityHeap* entities) {
+    entities->state_count[entities->entities[0]->state]--;
+    entities->type_count[entities->entities[0]->type]--;
+    entities->all_count[entities->entities[0]->state][entities->entities[0]->type]--;
+    entities->entities[0]->entered_in_queue = 0;
+
+    entities->entities[0] = entities->entities[bh_arr_length(entities->entities) - 1];
+    bh_arr_pop(entities->entities);
+    eh_shift_down(entities, 0);
+}
+
+void entity_change_type(EntityHeap* entities, Entity *ent, EntityType new_type) {
+    entities->type_count[ent->type]--;
+    entities->type_count[new_type]++;
+    ent->type = new_type;
+}
+
+// NOTE(Brendan Hansen): Uses the entity heap in the context structure
+void add_entities_for_node(bh_arr(Entity *) *target_arr, AstNode* node, Scope* scope, Package* package) {
+#define ENTITY_INSERT(_ent)                                     \
+    entity = entity_heap_register(entities, _ent);              \
+    if (target_arr) {                                           \
+        bh_arr(Entity *) __tmp_arr = *target_arr;               \
+        bh_arr_push(__tmp_arr, entity);                         \
+        *target_arr = __tmp_arr;                                \
+    } else {                                                    \
+        entity_heap_insert_existing(entities, entity);          \
+    }                                                           \
+
+    if (node->entity != NULL) return;
+
+    EntityHeap* entities = &context.entities;
+    Entity* entity;
+
+    Entity ent;
+    ent.id = entities->next_id++;
+    ent.state = Entity_State_Resolve_Symbols;
+    ent.package = package;
+    ent.scope   = scope;
+
+    switch (node->kind) {
+        case Ast_Kind_Load_All:
+        case Ast_Kind_Load_File: {
+            ent.type = Entity_Type_Load_File;
+            ent.include = (AstInclude *) node;
+            ENTITY_INSERT(ent);
+            break;
+        }
+
+        case Ast_Kind_Library_Path:
+        case Ast_Kind_Load_Path: {
+            ent.type = Entity_Type_Load_Path;
+            ent.include = (AstInclude *) node;
+            ENTITY_INSERT(ent);
+            break;
+        }
+
+        case Ast_Kind_Binding: {
+            ent.state   = Entity_State_Introduce_Symbols;
+            ent.type    = Entity_Type_Binding;
+            ent.binding = (AstBinding *) node;
+            ENTITY_INSERT(ent);
+            break;
+        }
+
+        case Ast_Kind_Function: {
+            if (((AstFunction *) node)->is_foreign != 0) {
+                ent.type     = Entity_Type_Foreign_Function_Header;
+                ent.function = (AstFunction *) node;
+                ENTITY_INSERT(ent);
+
+            } else {
+                ent.type     = Entity_Type_Function_Header;
+                ent.function = (AstFunction *) node;
+                ENTITY_INSERT(ent);
+                ((AstFunction *) node)->entity_header = entity;
+
+                ent.id       = entities->next_id++;
+                ent.type     = Entity_Type_Function;
+                ent.function = (AstFunction *) node;
+                ENTITY_INSERT(ent);
+                ((AstFunction *) node)->entity_body = entity;
+            }
+            break;
+        }
+
+        case Ast_Kind_Overloaded_Function: {
+            ent.type                = Entity_Type_Overloaded_Function;
+            ent.overloaded_function = (AstOverloadedFunction *) node;
+            ENTITY_INSERT(ent);
+            break;
+        }
+
+        case Ast_Kind_Global: {
+            ent.type   = Entity_Type_Global_Header;
+            ent.global = (AstGlobal *) node;
+            ENTITY_INSERT(ent);
+
+            ent.id     = entities->next_id++;
+            ent.type   = Entity_Type_Global;
+            ent.global = (AstGlobal *) node;
+            ENTITY_INSERT(ent);
+            break;
+        }
+
+        case Ast_Kind_StrLit: {
+            ent.type   = Entity_Type_String_Literal;
+            ent.strlit = (AstStrLit *) node;
+            ENTITY_INSERT(ent);
+            break;
+        }
+
+        case Ast_Kind_File_Contents: {
+            ent.type = Entity_Type_File_Contents;
+            ent.file_contents = (AstFileContents *) node;
+            ENTITY_INSERT(ent);
+            break;
+        }
+
+        case Ast_Kind_Struct_Type: {
+            ent.type = Entity_Type_Struct_Member_Default;
+            ent.type_alias = (AstType *) node;
+            ENTITY_INSERT(ent);
+            ((AstStructType *) node)->entity_defaults = entity;
+
+            ent.id       = entities->next_id++;
+            // fallthrough
+        }
+
+        case Ast_Kind_Poly_Struct_Type:
+        case Ast_Kind_Type_Alias: {
+            ent.type = Entity_Type_Type_Alias;
+            ent.type_alias = (AstType *) node;
+            ENTITY_INSERT(ent);
+
+            if (node->kind == Ast_Kind_Struct_Type) {
+                ((AstStructType *) node)->entity_type = entity;
+            }
+
+            break;
+        }
+
+        case Ast_Kind_Enum_Type: {
+            ent.type = Entity_Type_Enum;
+            ent.enum_type = (AstEnumType *) node;
+            ENTITY_INSERT(ent);
+            break;
+        }
+
+        case Ast_Kind_Enum_Value: {
+            ent.type = Entity_Type_Enum_Value;
+            ent.state = Entity_State_Check_Types;
+            ent.enum_value = (AstEnumValue *) node;
+            ENTITY_INSERT(ent);
+            break;
+        }
+
+        case Ast_Kind_Use: {
+            if (((AstUse *) node)->expr->kind == Ast_Kind_Package) {
+                ent.state = Entity_State_Resolve_Symbols;
+                ent.type = Entity_Type_Use_Package;
+            } else {
+                ent.type = Entity_Type_Use;
+            }
+
+            ent.use = (AstUse *) node;
+            ENTITY_INSERT(ent);
+            break;
+        }
+
+        case Ast_Kind_Memres: {
+            ent.type = Entity_Type_Memory_Reservation_Type;
+            ent.mem_res = (AstMemRes *) node;
+            ENTITY_INSERT(ent);
+            ((AstMemRes *) node)->type_entity = entity;
+
+            ent.id       = entities->next_id++;
+            ent.type = Entity_Type_Memory_Reservation;
+            ent.mem_res = (AstMemRes *) node;
+            ENTITY_INSERT(ent);
+            break;
+        }
+
+        case Ast_Kind_Macro: {
+            ent.type = Entity_Type_Macro;
+            ent.macro = (AstMacro *) node;
+            ENTITY_INSERT(ent);
+            break;
+        }
+
+        case Ast_Kind_Polymorphic_Proc: {
+            ent.type = Entity_Type_Polymorphic_Proc;
+            ent.poly_proc = (AstFunction *) node;
+            ENTITY_INSERT(ent);
+            break;
+        }
+
+        case Ast_Kind_Polymorph_Query: {
+            ent.type = Entity_Type_Polymorph_Query;
+            ent.state = Entity_State_Check_Types;
+            ent.poly_query = (AstPolyQuery *) node;
+            ENTITY_INSERT(ent);
+            break;
+        }
+
+        case Ast_Kind_Static_If: {
+            ent.state = Entity_State_Resolve_Symbols;
+            ent.type = Entity_Type_Static_If;
+            ent.static_if = (AstIf *) node;
+            ENTITY_INSERT(ent);
+            break;
+        }
+
+        case Ast_Kind_Directive_Error: {
+            ent.state = Entity_State_Error;
+            ent.type = Entity_Type_Error;
+            ent.error = (AstDirectiveError *) node;
+            ENTITY_INSERT(ent);
+            break;
+        }
+
+        case Ast_Kind_Directive_Export:
+        case Ast_Kind_Directive_Add_Overload:
+        case Ast_Kind_Directive_Operator:
+        case Ast_Kind_Directive_Init:
+        case Ast_Kind_Directive_Library:
+        case Ast_Kind_Injection: {
+            ent.type = Entity_Type_Process_Directive;
+            ent.expr = (AstTyped *) node;
+            ENTITY_INSERT(ent);
+            break;
+        }
+
+        case Ast_Kind_Note: {
+            ent.type = Entity_Type_Note;
+            ent.expr = (AstTyped *) node;
+            ent.state = Entity_State_Code_Gen;
+            ENTITY_INSERT(ent);
+            break;
+        }
+
+        case Ast_Kind_Interface: {
+            ent.type = Entity_Type_Interface;
+            ent.interface = (AstInterface *) node;
+            ent.state = Entity_State_Resolve_Symbols;
+            ENTITY_INSERT(ent);
+            break;
+        }
+
+        case Ast_Kind_Constraint: {
+            ent.type = Entity_Type_Constraint_Check;
+            ent.constraint = (AstConstraint *) node;
+            ent.state = Entity_State_Resolve_Symbols;
+            ENTITY_INSERT(ent);
+            break;
+        }
+
+        case Ast_Kind_Foreign_Block: {
+            ent.type = Entity_Type_Foreign_Block;
+            ent.foreign_block = (AstForeignBlock *) node;
+            ent.state = Entity_State_Resolve_Symbols;
+            ENTITY_INSERT(ent);
+            break;
+        }
+
+        default: {
+            ent.type = Entity_Type_Expression;
+            ent.expr = (AstTyped *) node;
+            ENTITY_INSERT(ent);
+            break;
+        }
+    }
+
+    node->entity = entity;
+}
diff --git a/compiler/src/errors.c b/compiler/src/errors.c
new file mode 100644 (file)
index 0000000..f6811ca
--- /dev/null
@@ -0,0 +1,156 @@
+#include "errors.h"
+#include "utils.h"
+
+OnyxErrors errors;
+
+void onyx_errors_init(bh_arr(bh_file_contents)* files) {
+    errors.file_contents = files;
+
+    bh_arena_init(&errors.msg_arena, global_heap_allocator, 16 * 1024);
+    errors.msg_alloc = bh_arena_allocator(&errors.msg_arena);
+
+    bh_arr_new(global_heap_allocator, errors.errors, 4);
+}
+
+static void print_detailed_message(OnyxError* err, bh_file_contents* fc) {
+    bh_printf("(%s:%l,%l) %s\n", err->pos.filename, err->pos.line, err->pos.column, err->text);
+
+    b32 colored_printing = 0;
+    #ifdef _BH_LINUX
+        colored_printing = !context.options->no_colors;
+    #endif
+
+    i32 linelength = 0;
+    i32 first_char = 0;
+    char* walker = err->pos.line_start;
+    while (*walker == ' ' || *walker == '\t') first_char++, linelength++, walker++;
+    while (*walker != '\n') linelength++, walker++;
+
+    if (colored_printing) bh_printf("\033[90m");
+    i32 numlen = bh_printf(" %d | ", err->pos.line);
+    if (colored_printing) bh_printf("\033[94m");
+    bh_printf("%b\n", err->pos.line_start, linelength);
+
+    char* pointer_str = bh_alloc_array(global_scratch_allocator, char, linelength + numlen);
+    memset(pointer_str, ' ', linelength + numlen);
+    memcpy(pointer_str + numlen - 1, err->pos.line_start, first_char);
+    memset(pointer_str + first_char + numlen - 1, ' ', err->pos.column - first_char);
+    memset(pointer_str + err->pos.column + numlen - 1, '~', err->pos.length - 1);
+    pointer_str[err->pos.column + numlen - 2] = '^';
+    pointer_str[err->pos.column + numlen + err->pos.length - 1] = 0;
+
+    if (colored_printing) bh_printf("\033[91m");
+    bh_printf("%s\n", pointer_str);
+
+    if (colored_printing) bh_printf("\033[0m");
+}
+
+static i32 errors_sort(const void* v1, const void* v2) {
+    OnyxError* e1 = (OnyxError *) v1;
+    OnyxError* e2 = (OnyxError *) v2;
+    return e2->rank - e1->rank;
+}
+
+void onyx_errors_print() {
+    // NOTE: If the format of the error messages is ever changed,
+    // update onyx_compile.vim and onyx.sublime-build to match
+    // the new format. This was editor error highlighting is still
+    // supported.
+    //
+    //                                      - brendanfh   2020/09/03
+
+    qsort(errors.errors, bh_arr_length(errors.errors), sizeof(OnyxError), errors_sort);
+
+    OnyxErrorRank last_rank = errors.errors[0].rank;
+    bh_arr_each(OnyxError, err, errors.errors) {
+        if (last_rank != err->rank) break;
+
+        if (err->pos.filename) {
+            bh_file_contents file_contents = { 0 };
+            bh_arr_each(bh_file_contents, fc, *errors.file_contents) {
+                if (!strcmp(fc->filename, err->pos.filename)) {
+                    file_contents = *fc;
+                    break;
+                }
+            }
+
+            print_detailed_message(err, &file_contents);
+
+        } else {
+            bh_printf("(%l,%l) %s\n", err->pos.line, err->pos.column, err->text);
+        }
+
+        last_rank = err->rank;
+    }
+}
+
+b32 onyx_has_errors() {
+    return bh_arr_length(errors.errors) > 0;
+}
+
+void onyx_clear_errors() {
+    if (context.cycle_detected) return;
+
+    bh_arr_set_length(errors.errors, 0);
+}
+
+void onyx_submit_error(OnyxError error) {
+    bh_arr_push(errors.errors, error);
+}
+
+void onyx_report_error(OnyxFilePos pos, OnyxErrorRank rank, char * format, ...) {
+
+    va_list vargs;
+    va_start(vargs, format);
+    char* msg = bh_bprintf_va(format, vargs);
+    va_end(vargs);
+
+    OnyxError err = {
+        .pos = pos,
+        .rank = rank,
+        .text = bh_strdup(errors.msg_alloc, msg),
+    };
+
+    bh_arr_push(errors.errors, err);
+}
+
+void onyx_submit_warning(OnyxError error) {
+    bh_file_contents file_contents = { 0 };
+    bh_arr_each(bh_file_contents, fc, *errors.file_contents) {
+        if (!strcmp(fc->filename, error.pos.filename)) {
+            file_contents = *fc;
+            break;
+        }
+    }
+
+    print_detailed_message(&error, &file_contents);
+}
+
+// This definitely doesn't do what I thought it did?
+void onyx_report_warning(OnyxFilePos pos, char* format, ...) {
+    va_list vargs;
+    va_start(vargs, format);
+    char* msg = bh_bprintf_va(format, vargs);
+    va_end(vargs);
+
+    OnyxError err = {
+        .pos = pos,
+        .rank = Error_Warning,
+        .text = msg,
+    };
+
+    bh_arr_push(errors.errors, err);
+
+    /*
+
+    bh_file_contents file_contents = { 0 };
+    bh_arr_each(bh_file_contents, fc, *errors.file_contents) {
+        if (!strcmp(fc->filename, pos.filename)) {
+            file_contents = *fc;
+            break;
+        }
+    }
+
+    print_detailed_message(&err, &file_contents);
+    */
+}
diff --git a/compiler/src/lex.c b/compiler/src/lex.c
new file mode 100644 (file)
index 0000000..eba315e
--- /dev/null
@@ -0,0 +1,548 @@
+#include "bh.h"
+#include "lex.h"
+#include "utils.h"
+#include "errors.h"
+
+u64 lexer_lines_processed = 0;
+u64 lexer_tokens_processed = 0;
+
+static const char* token_type_names[] = {
+    "TOKEN_TYPE_UNKNOWN",
+    "TOKEN_TYPE_END_STREAM",
+
+    "TOKEN_TYPE_COMMENT",
+
+    "package",
+    "struct",
+    "enum",
+    "use",
+    "if",
+    "else",
+    "elseif",
+    "return",
+    "global",
+    "cast",
+    "while",
+    "for",
+    "break",
+    "continue",
+    "sizeof",
+    "alignof",
+    "typeof",
+    "defer",
+    "do",
+    "case",
+    "switch",
+    "fallthrough",
+    "macro",
+    "interface",
+    "where",
+
+    "->",
+    "<-",
+    "---",
+    "|>",
+
+    ">=",
+    "<=",
+    "==",
+    "!=",
+    "+=",
+    "-=",
+    "*=",
+    "/=",
+    "%=",
+    "&=",
+    "|=",
+    "^=",
+    "&&",
+    "||",
+    "<<",
+    ">>",
+    ">>>",
+    "<<=",
+    ">>=",
+    ">>>=",
+    "..",
+    "~~",
+
+    "TOKEN_TYPE_SYMBOL",
+    "TOKEN_TYPE_LITERAL_STRING",
+    "TOKEN_TYPE_LITERAL_INTEGER",
+    "TOKEN_TYPE_LITERAL_FLOAT",
+    "true",
+    "false",
+
+    "NOTE",
+
+    "TOKEN_TYPE_COUNT"
+};
+
+#ifndef LITERAL_TOKEN
+#define LITERAL_TOKEN(token, word, token_type) \
+    if (token_lit(tokenizer, &tk, token, word, token_type)) goto token_parsed;
+#endif
+
+#ifndef INCREMENT_CURR_TOKEN
+#define INCREMENT_CURR_TOKEN(tkn) { \
+    if (*(tkn)->curr == '\n') { \
+        (tkn)->line_number++; \
+        (tkn)->line_start = (tkn)->curr + 1; \
+    } \
+    (tkn)->curr++; \
+}
+#endif
+
+#define char_is_alphanum(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z') || ((c) >= '0' && (c) <= '9'))
+#define char_is_num(c)      ((c) >= '0' && (c) <= '9')
+#define char_is_alpha(c)    (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z'))
+
+static inline b32 token_lit(OnyxTokenizer* tokenizer, OnyxToken* tk, char* lit, b32 is_word, TokenType type) {
+    i64 len = 0;
+    char* ptr1 = tokenizer->curr;
+    char* ptr2 = lit;
+    while (*ptr2 != '\0' && *ptr1 == *ptr2) ptr1++, ptr2++, len++;
+    if (*ptr2 != '\0') return 0;
+
+    if (is_word && (char_is_alphanum(*ptr1) || *ptr1 == '_'))
+        return 0;
+
+    tk->type = type;
+    tk->text = tokenizer->curr;
+    tk->length = len;
+    tk->pos.line = tokenizer->line_number;
+    tk->pos.column = (i32)(tokenizer->curr - tokenizer->line_start) + 1;
+
+    tokenizer->curr += len;
+
+    return 1;
+}
+
+const char* token_name(TokenType tkn_type) {
+    if (tkn_type < Token_Type_Ascii_End) {
+        return bh_aprintf(global_scratch_allocator, "%c", (char) tkn_type);
+    } else {
+        return token_type_names[tkn_type - Token_Type_Ascii_End];
+    }
+}
+
+void token_toggle_end(OnyxToken* tkn) {
+    static char backup = 0;
+    char tmp = tkn->text[tkn->length];
+    tkn->text[tkn->length] = backup;
+    backup = tmp;
+}
+
+OnyxToken* onyx_get_token(OnyxTokenizer* tokenizer) {
+    OnyxToken tk;
+
+    // Skip whitespace
+    while (1) {
+        if (tokenizer->curr == tokenizer->end) break;
+
+        switch (*tokenizer->curr) {
+            case ' ':
+            case '\n':
+            case '\t':
+            case '\r':
+                INCREMENT_CURR_TOKEN(tokenizer);
+                break;
+            default:
+                goto whitespace_skipped;
+        }
+    }
+
+whitespace_skipped:
+
+    tk.type = Token_Type_Unknown;
+    tk.text = tokenizer->curr;
+    tk.length = 1;
+    tk.pos.line_start = tokenizer->line_start;
+    tk.pos.filename = tokenizer->filename;
+    tk.pos.line = tokenizer->line_number;
+    tk.pos.column = (u16)(tokenizer->curr - tokenizer->line_start) + 1;
+
+    if (tokenizer->curr == tokenizer->end) {
+        tk.type = Token_Type_End_Stream;
+        goto token_parsed;
+    }
+
+    // She-bang
+    if (tokenizer->curr == tokenizer->start) {
+        if (*tokenizer->curr == '#' && *(tokenizer->curr + 1) == '!') {
+            tk.type = Token_Type_Comment;
+
+            tokenizer->curr += 2;
+            while (*tokenizer->curr++ != '\n' && tokenizer->curr != tokenizer->end);
+
+            tk.length = tokenizer->curr - tk.text;
+            goto token_parsed;
+        }
+    }
+
+    // Comments
+    if (*tokenizer->curr == '/' && *(tokenizer->curr + 1) == '/') {
+        tokenizer->curr += 2;
+        tk.type = Token_Type_Comment;
+        tk.text = tokenizer->curr;
+
+        while (*tokenizer->curr != '\n' && tokenizer->curr != tokenizer->end) {
+            INCREMENT_CURR_TOKEN(tokenizer);
+        }
+
+        tk.length = tokenizer->curr - tk.text - 2;
+        goto token_parsed;
+    }
+
+    if (*tokenizer->curr == '/' && *(tokenizer->curr + 1) == '*') {
+        tokenizer->curr += 2;
+        tk.type = Token_Type_Comment;
+        tk.text = tokenizer->curr;
+
+        i32 comment_depth = 1;
+
+        while (comment_depth > 0 && tokenizer->curr != tokenizer->end) {
+            if (*tokenizer->curr == '/' && *(tokenizer->curr + 1) == '*') {
+                tokenizer->curr += 2;
+                comment_depth += 1;
+            }
+
+            else if (*tokenizer->curr == '*' && *(tokenizer->curr + 1) == '/') {
+                tokenizer->curr += 2;
+                comment_depth -= 1;
+            }
+
+            else {
+                INCREMENT_CURR_TOKEN(tokenizer);
+            }
+        }
+
+        goto token_parsed;
+    }
+
+    if (*tk.text == '"' && *(tk.text + 1) == '"' && *(tk.text + 2) == '"') {
+        u64 len = 0;
+
+        INCREMENT_CURR_TOKEN(tokenizer);
+        INCREMENT_CURR_TOKEN(tokenizer);
+        INCREMENT_CURR_TOKEN(tokenizer);
+
+        while (!(*tokenizer->curr == '"' && *(tokenizer->curr + 1) == '"' && *(tokenizer->curr + 2) == '"')) {
+            len++;
+            INCREMENT_CURR_TOKEN(tokenizer);
+        }
+        
+        INCREMENT_CURR_TOKEN(tokenizer);
+        INCREMENT_CURR_TOKEN(tokenizer);
+        INCREMENT_CURR_TOKEN(tokenizer);
+
+        tk.text += 3;
+        tk.length = len;
+        tk.type = Token_Type_Literal_String;
+        goto token_parsed;
+    }
+
+    // String literal
+    if (*tk.text == '"') {
+        u64 len = 0;
+        u64 slash_count = 0;
+
+        INCREMENT_CURR_TOKEN(tokenizer);
+
+        while (!(*tokenizer->curr == '"' && slash_count == 0)) {
+            len++;
+
+            // if (*tokenizer->curr == '\n') {
+            //     onyx_report_error(tk.pos, "String literal not terminated by end of line.");
+            // }
+
+            if (*tokenizer->curr == '\\') {
+                slash_count += 1;
+                slash_count %= 2;
+            } else {
+                slash_count = 0;
+            }
+
+            INCREMENT_CURR_TOKEN(tokenizer);
+        }
+
+        INCREMENT_CURR_TOKEN(tokenizer);
+
+        tk.text++;
+        tk.type = Token_Type_Literal_String;
+        tk.length = len;
+        goto token_parsed;
+    }
+
+    // Hex literal
+    if (*tokenizer->curr == '0' && *(tokenizer->curr + 1) == 'x' && charset_contains("0123456789abcdefABCDEF", *(tokenizer->curr + 2))) {
+        INCREMENT_CURR_TOKEN(tokenizer);
+        INCREMENT_CURR_TOKEN(tokenizer);
+        u32 len = 3;
+        while (char_is_num(*(tokenizer->curr + 1)) || charset_contains("abcdefABCDEF", *(tokenizer->curr + 1))) {
+            len++;
+            INCREMENT_CURR_TOKEN(tokenizer);
+        }
+
+        tk.type = Token_Type_Literal_Integer;
+        tk.length = len;
+
+        INCREMENT_CURR_TOKEN(tokenizer);
+        goto token_parsed;
+    }
+
+    if (*tokenizer->curr == '@') {
+        INCREMENT_CURR_TOKEN(tokenizer);
+        u32 len = 2;
+        while (char_is_alphanum(*(tokenizer->curr + 1)) || *(tokenizer->curr + 1) == '_') {
+            len++;
+            INCREMENT_CURR_TOKEN(tokenizer);
+        }
+
+        tk.type = Token_Type_Note;
+        tk.length = len;
+        INCREMENT_CURR_TOKEN(tokenizer);
+        goto token_parsed;
+    }
+
+    // Number literal
+    if (char_is_num(*tokenizer->curr)
+        || (*(tokenizer->curr) == '.' && char_is_num(*(tokenizer->curr + 1)))) {
+        tk.type = Token_Type_Literal_Integer;
+
+        b32 hit_decimal = 0;
+        if (*tokenizer->curr == '.') hit_decimal = 1;
+
+        u32 len = 1;
+        while (char_is_num(*(tokenizer->curr + 1))
+            || (!hit_decimal && *(tokenizer->curr + 1) == '.' && *(tokenizer->curr + 2) != '.')) {
+            len++;
+            INCREMENT_CURR_TOKEN(tokenizer);
+
+            if (*tokenizer->curr == '.') hit_decimal = 1;
+        }
+
+        if (*(tokenizer->curr + 1) == 'f') {
+            tk.type = Token_Type_Literal_Float;
+
+            len++;
+            INCREMENT_CURR_TOKEN(tokenizer);
+        }
+
+        if (hit_decimal) tk.type = Token_Type_Literal_Float;
+
+        tk.length = len;
+
+        INCREMENT_CURR_TOKEN(tokenizer);
+        goto token_parsed;
+    }
+
+    char curr = *tokenizer->curr;
+    switch (curr) {
+    case 'a':
+        LITERAL_TOKEN("alignof",     1, Token_Type_Keyword_Alignof);
+        break;
+    case 'b':
+        LITERAL_TOKEN("break",       1, Token_Type_Keyword_Break);
+        break;
+    case 'c':
+        LITERAL_TOKEN("case",        1, Token_Type_Keyword_Case);
+        LITERAL_TOKEN("cast",        1, Token_Type_Keyword_Cast);
+        LITERAL_TOKEN("continue",    1, Token_Type_Keyword_Continue);
+        break;
+    case 'd':
+        LITERAL_TOKEN("defer",       1, Token_Type_Keyword_Defer);
+        LITERAL_TOKEN("do",          1, Token_Type_Keyword_Do);
+        break;
+    case 'e':
+        LITERAL_TOKEN("enum",        1, Token_Type_Keyword_Enum);
+        LITERAL_TOKEN("elseif",      1, Token_Type_Keyword_Elseif);
+        LITERAL_TOKEN("else",        1, Token_Type_Keyword_Else);
+        break;
+    case 'f':
+        LITERAL_TOKEN("for",         1, Token_Type_Keyword_For);
+        LITERAL_TOKEN("false",       1, Token_Type_Literal_False);
+        LITERAL_TOKEN("fallthrough", 1, Token_Type_Keyword_Fallthrough);
+        break;
+    case 'g':
+        LITERAL_TOKEN("global",      1, Token_Type_Keyword_Global);
+        break;
+    case 'i':
+        LITERAL_TOKEN("if",          1, Token_Type_Keyword_If);
+        LITERAL_TOKEN("interface",   1, Token_Type_Keyword_Interface);
+        break;
+    case 'm':
+        LITERAL_TOKEN("macro",       1, Token_Type_Keyword_Macro);
+        break;
+    case 'p':
+        LITERAL_TOKEN("package",     1, Token_Type_Keyword_Package);
+        break;
+    case 'r':
+        LITERAL_TOKEN("return",      1, Token_Type_Keyword_Return);
+        break;
+    case 's':
+        LITERAL_TOKEN("struct",      1, Token_Type_Keyword_Struct);
+        LITERAL_TOKEN("sizeof",      1, Token_Type_Keyword_Sizeof);
+        LITERAL_TOKEN("switch",      1, Token_Type_Keyword_Switch);
+        break;
+    case 't':
+        LITERAL_TOKEN("true",        1, Token_Type_Literal_True);
+        LITERAL_TOKEN("typeof",      1, Token_Type_Keyword_Typeof);
+        break;
+    case 'u':
+        LITERAL_TOKEN("use",         1, Token_Type_Keyword_Use);
+        break;
+    case 'w':
+        LITERAL_TOKEN("while",       1, Token_Type_Keyword_While);
+        LITERAL_TOKEN("where",       1, Token_Type_Keyword_Where);
+        break;
+
+    case '-':
+        LITERAL_TOKEN("->",          0, Token_Type_Right_Arrow);
+        LITERAL_TOKEN("---",         0, Token_Type_Empty_Block);
+        LITERAL_TOKEN("-=",          0, Token_Type_Minus_Equal);
+        break;
+
+    case '<':
+        LITERAL_TOKEN("<=",          0, Token_Type_Less_Equal);
+        LITERAL_TOKEN("<-",          0, Token_Type_Right_Arrow);
+        LITERAL_TOKEN("<<=",         0, Token_Type_Shl_Equal);
+        LITERAL_TOKEN("<<",          0, Token_Type_Shift_Left);
+        break;
+
+    case '>':
+        LITERAL_TOKEN(">=",          0, Token_Type_Greater_Equal);
+        LITERAL_TOKEN(">>>=",        0, Token_Type_Sar_Equal);
+        LITERAL_TOKEN(">>=",         0, Token_Type_Shr_Equal);
+        LITERAL_TOKEN(">>>",         0, Token_Type_Shift_Arith_Right);
+        LITERAL_TOKEN(">>",          0, Token_Type_Shift_Right);
+        break;
+
+    case '&':
+        LITERAL_TOKEN("&&",          0, Token_Type_And_And);
+        LITERAL_TOKEN("&=",          0, Token_Type_And_Equal);
+        break;
+
+    case '|':
+        LITERAL_TOKEN("|>",          0, Token_Type_Pipe);
+        LITERAL_TOKEN("||",          0, Token_Type_Or_Or);
+        LITERAL_TOKEN("|=",          0, Token_Type_Or_Equal);
+        break;
+
+    case '=':
+        LITERAL_TOKEN("==",          0, Token_Type_Equal_Equal);
+        break;
+
+    case '!':
+        LITERAL_TOKEN("!=",          0, Token_Type_Not_Equal);
+        break;
+
+    case '+':
+        LITERAL_TOKEN("+=",          0, Token_Type_Plus_Equal);
+        break;
+
+    case '*':
+        LITERAL_TOKEN("*=",          0, Token_Type_Star_Equal);
+        break;
+
+    case '^':
+        LITERAL_TOKEN("^=",          0, Token_Type_Xor_Equal);
+        break;
+
+    case '/':
+        LITERAL_TOKEN("/=",          0, Token_Type_Fslash_Equal);
+        break;
+
+    case '%':
+        LITERAL_TOKEN("%=",          0, Token_Type_Percent_Equal);
+        break;
+
+    case '.':
+        LITERAL_TOKEN("..",          0, Token_Type_Dot_Dot);
+        break;
+
+    case '~':
+        LITERAL_TOKEN("~~",          0, Token_Type_Tilde_Tilde);
+        break;
+    }
+
+    // Symbols
+    if (char_is_alpha(*tk.text) || *tokenizer->curr == '_') {
+        u64 len = 0;
+        while (char_is_alphanum(*tokenizer->curr) || *tokenizer->curr == '_') {
+            len++;
+            INCREMENT_CURR_TOKEN(tokenizer);
+        }
+
+        tk.length = len;
+        tk.type = Token_Type_Symbol;
+        goto token_parsed;
+    }
+
+
+    tk.type = (TokenType) *tokenizer->curr;
+    INCREMENT_CURR_TOKEN(tokenizer);
+
+token_parsed:
+    tk.pos.length = (u16) tk.length;
+    bh_arr_push(tokenizer->tokens, tk);
+
+    return &tokenizer->tokens[bh_arr_length(tokenizer->tokens) - 1];
+}
+
+OnyxTokenizer onyx_tokenizer_create(bh_allocator allocator, bh_file_contents *fc) {
+    OnyxTokenizer tknizer = {
+        .start          = fc->data,
+        .curr           = fc->data,
+        .end            = bh_pointer_add(fc->data, fc->length),
+
+        .filename       = fc->filename,
+
+        .line_number    = 1,
+        .line_start     = fc->data,
+        .tokens         = NULL,
+    };
+
+    bh_arr_new(allocator, tknizer.tokens, 1 << 12);
+    return tknizer;
+}
+
+void onyx_tokenizer_free(OnyxTokenizer* tokenizer) {
+    bh_arr_free(tokenizer->tokens);
+}
+
+void onyx_lex_tokens(OnyxTokenizer* tokenizer) {
+    OnyxToken* tk;
+    do {
+        tk = onyx_get_token(tokenizer);
+    } while (tk->type != Token_Type_End_Stream);
+
+    lexer_lines_processed += tokenizer->line_number - 1;
+    lexer_tokens_processed += bh_arr_length(tokenizer->tokens);
+}
+
+b32 token_equals(OnyxToken* tkn1, OnyxToken* tkn2) {
+    if (tkn1->length != tkn2->length) return 0;
+    fori (i, 0, tkn1->length)
+        if (tkn1->text[i] != tkn2->text[i]) return 0;
+    return 1;
+}
+
+b32 token_text_equals(OnyxToken* tkn, char* text) {
+    i32 text_len = strlen(text);
+    if (tkn->length != text_len) return 0;
+
+    fori (i, 0, tkn->length)
+        if (tkn->text[i] != text[i]) return 0;
+        
+    return 1;
+}
+
+b32 token_same_file(OnyxToken *tkn1, OnyxToken *tkn2) {
+    if (!tkn1 || !tkn2) return 0;
+    
+    if (tkn1->pos.filename == tkn2->pos.filename) return 1;
+
+    // :Security?
+    return strcmp(tkn1->pos.filename, tkn2->pos.filename) == 0;
+}
diff --git a/compiler/src/onyx.c b/compiler/src/onyx.c
new file mode 100644 (file)
index 0000000..9433df3
--- /dev/null
@@ -0,0 +1,800 @@
+// #define BH_DEBUG
+#define BH_DEFINE
+#define BH_NO_TABLE
+#define STB_DS_IMPLEMENTATION
+#include "bh.h"
+
+#include "lex.h"
+#include "errors.h"
+#include "parser.h"
+#include "utils.h"
+#include "wasm_emit.h"
+#include "doc.h"
+
+#define VERSION "v0.1.0"
+
+
+// #ifndef CORE_INSTALLATION
+//     #ifdef _BH_LINUX
+//     #define CORE_INSTALLATION "/usr/share/onyx"
+//     #elif defined(_WIN32) || defined(_WIN64)
+//     #define CORE_INSTALLATION "\\dev\\onyx\\"
+//     #endif
+// #endif
+
+
+
+
+Context context;
+
+
+static const char* docstring = "Onyx compiler version " VERSION "\n"
+    "\n"
+    "The compiler for the Onyx programming language, created by Brendan Hansen.\n"
+    "\n"
+    "Usage:\n"
+    "\tonyx compile [-o <target file>] [--verbose] <input files>\n"
+#ifdef ENABLE_RUN_WITH_WASMER
+    "\tonyx run <input files> -- <args>\n"
+#endif
+    // "\tonyx doc <input files>\n"
+    "\tonyx help\n"
+    "\n"
+    "Flags:\n"
+    "\t<input files>           List of initial files\n"
+    "\t-o <target_file>        Specify the target file (default: out.wasm).\n"
+    "\t--runtime, -r <runtime> Specifies the runtime. Can be: onyx, wasi, js, custom.\n"
+    "\t--verbose, -V           Verbose output.\n"
+    "\t           -VV          Very verbose output.\n"
+    "\t           -VVV         Very very verbose output (to be used by compiler developers).\n"
+    "\t--wasm-mvp              Use only WebAssembly MVP features.\n"
+    "\t--multi-threaded        Enables multi-threading for this compilation.\n"
+    "\t--doc <doc_file>\n"
+    "\t--generate-foreign-info\n"
+    "\n"
+    "Developer flags:\n"
+    "\t--print-function-mappings Prints a mapping from WASM function index to source location.\n"
+    "\t--print-static-if-results Prints the conditional result of each #if statement. Useful for debugging.\n"
+    "\t--print-notes             Prints the location of notes throughout the loaded code.\n"
+    "\t--no-colors               Disables colors in the error message.\n"
+    "\t--no-file-contents        Disables '#file_contents' for security.\n"
+    "\n";
+
+
+static CompileOptions compile_opts_parse(bh_allocator alloc, int argc, char *argv[]) {
+    CompileOptions options = {
+        .allocator = alloc,
+        .action = ONYX_COMPILE_ACTION_PRINT_HELP,
+
+        .verbose_output          = 0,
+        .fun_output              = 0,
+        .print_function_mappings = 0,
+        .no_file_contents        = 0,
+
+        .use_post_mvp_features   = 1,
+        .use_multi_threading     = 0,
+
+        .runtime = Runtime_Onyx,
+
+        .files = NULL,
+        .target_file = "out.wasm",
+
+        .documentation_file = NULL,
+
+        .debug_enabled = 0,
+
+        .passthrough_argument_count = 0,
+        .passthrough_argument_data  = NULL,
+    };
+
+    bh_arr_new(alloc, options.files, 2);
+    bh_arr_new(alloc, options.included_folders, 2);
+
+    char* core_installation;
+    #ifdef _BH_LINUX
+    core_installation = CORE_INSTALLATION;
+    #endif
+    #ifdef _BH_WINDOWS
+    core_installation = bh_alloc_array(global_heap_allocator, u8, 512);
+    GetEnvironmentVariableA("ONYX_PATH", core_installation, 512);
+    #endif
+
+    // NOTE: Add the current folder
+    bh_arr_push(options.included_folders, core_installation);
+    bh_arr_push(options.included_folders, ".");
+
+    if (argc == 1) return options;
+    i32 arg_parse_start = 1;
+
+    if (!strcmp(argv[1], "help"))     options.action = ONYX_COMPILE_ACTION_PRINT_HELP;
+    if (!strcmp(argv[1], "compile")) {
+        options.action = ONYX_COMPILE_ACTION_COMPILE;
+        arg_parse_start = 2;
+    }
+    #ifdef ENABLE_RUN_WITH_WASMER
+    else if (!strcmp(argv[1], "run")) {
+        options.action = ONYX_COMPILE_ACTION_RUN;
+        arg_parse_start = 2;
+    }
+    #endif
+    else options.action = ONYX_COMPILE_ACTION_COMPILE;
+
+    if (options.action != ONYX_COMPILE_ACTION_PRINT_HELP) {
+        fori(i, arg_parse_start, argc) {
+            if (!strcmp(argv[i], "-o")) {
+                options.target_file = argv[++i];
+            }
+            else if (!strcmp(argv[i], "--verbose") || !strcmp(argv[i], "-V")) {
+                options.verbose_output = 1;
+            }
+            else if (!strcmp(argv[i], "-VV")) {
+                options.verbose_output = 2;
+            }
+            else if (!strcmp(argv[i], "-VVV")) {
+                options.verbose_output = 3;
+            }
+            else if (!strcmp(argv[i], "--print-function-mappings")) {
+                options.print_function_mappings = 1;
+            }
+            else if (!strcmp(argv[i], "--print-static-if-results")) {
+                options.print_static_if_results = 1;
+            }
+            else if (!strcmp(argv[i], "--print-notes")) {
+                options.print_notes = 1;
+            }
+            else if (!strcmp(argv[i], "--no-colors")) {
+                options.no_colors = 1;
+            }
+            else if (!strcmp(argv[i], "--no-file-contents")) {
+                options.no_file_contents = 1;
+            }
+            else if (!strcmp(argv[i], "--wasm-mvp")) {
+                options.use_post_mvp_features = 0;
+            }
+            else if (!strcmp(argv[i], "--multi-threaded")) {
+                options.use_multi_threading = 1;
+            }
+            else if (!strcmp(argv[i], "--generate-foreign-info")) {
+                options.generate_foreign_info = 1;
+            }
+            else if (!strcmp(argv[i], "-I")) {
+                bh_arr_push(options.included_folders, argv[++i]);
+            }
+            else if (!strcmp(argv[i], "-r") || !strcmp(argv[i], "--runtime")) {
+                i += 1;
+                if      (!strcmp(argv[i], "onyx"))   options.runtime = Runtime_Onyx;
+                else if (!strcmp(argv[i], "wasi"))   options.runtime = Runtime_Wasi;
+                else if (!strcmp(argv[i], "js"))     options.runtime = Runtime_Js;
+                else if (!strcmp(argv[i], "custom")) options.runtime = Runtime_Custom;
+                else {
+                    bh_printf("WARNING: '%s' is not a valid runtime. Defaulting to 'onyx'.\n", argv[i]);
+                    options.runtime = Runtime_Wasi;
+                }
+            }
+            else if (!strcmp(argv[i], "--doc")) {
+                options.documentation_file = argv[++i];
+            }
+            else if (!strcmp(argv[i], "--debug")) {
+                options.debug_enabled = 1;
+            }
+            else if (!strcmp(argv[i], "--")) {
+                options.passthrough_argument_count = argc - i - 1;
+                options.passthrough_argument_data  = &argv[i + 1];
+                break;
+            }
+#if defined(_BH_LINUX)
+            // NOTE: Fun output is only enabled for Linux because Windows command line
+            // is not ANSI compatible and for a silly feature, I don't want to learn
+            // how to properly do arbitrary graphics in it.
+            else if (!strcmp(argv[i], "--fun") || !strcmp(argv[i], "-F")) {
+                options.fun_output = 1;
+            }
+#endif
+            else {
+                bh_arr_push(options.files, argv[i]);
+            }
+        }
+    }
+
+    // NOTE: Always enable multi-threading for the Onyx runtime.
+    if (options.runtime == Runtime_Onyx) {
+        options.use_multi_threading = 1;
+    }
+
+    return options;
+}
+
+static void compile_opts_free(CompileOptions* opts) {
+    bh_arr_free(opts->files);
+    bh_arr_free(opts->included_folders);
+}
+
+
+
+
+
+typedef enum CompilerProgress {
+    ONYX_COMPILER_PROGRESS_ERROR,
+    ONYX_COMPILER_PROGRESS_FAILED_OUTPUT,
+    ONYX_COMPILER_PROGRESS_SUCCESS
+} CompilerProgress;
+
+static OnyxToken implicit_load_token = { '#', 1, 0, { 0, 0, 0, 0, 0 } };
+static AstInclude* create_load(bh_allocator alloc, char* filename) {
+    AstInclude* include_node = onyx_ast_node_new(alloc, sizeof(AstInclude), Ast_Kind_Load_File);
+    include_node->name = filename;
+    include_node->token = &implicit_load_token;
+
+    return include_node;
+}
+
+// HACK
+static u32 special_global_entities_remaining = 3;
+static Entity *runtime_info_types_entity;
+static Entity *runtime_info_foreign_entity;
+static Entity *runtime_info_proc_tags_entity;
+
+static void context_init(CompileOptions* opts) {
+    types_init();
+
+    context.options = opts;
+    context.cycle_detected = 0;
+    context.cycle_almost_detected = 0;
+
+    OnyxFilePos internal_location = { 0 };
+    internal_location.filename = "<compiler internal>";
+    internal_location.line     = 1;
+    internal_location.column   = 1;
+    context.global_scope = scope_create(global_heap_allocator, NULL, internal_location);
+    sh_new_arena(context.packages);
+
+    // NOTE: This will be initialized upon the first call to entity_heap_insert.
+    context.entities.next_id  = 0;
+    context.entities.entities = NULL;
+
+    onyx_errors_init(&context.loaded_files);
+
+    context.token_alloc = global_heap_allocator;
+
+    // NOTE: Create the arena where tokens and AST nodes will exist
+    // Prevents nodes from being scattered across memory due to fragmentation
+    bh_arena_init(&context.ast_arena, global_heap_allocator, 16 * 1024 * 1024); // 16MB
+    context.ast_alloc = bh_arena_allocator(&context.ast_arena);
+
+    context.wasm_module = bh_alloc_item(global_heap_allocator, OnyxWasmModule);
+    *context.wasm_module = onyx_wasm_module_create(global_heap_allocator);
+
+    entity_heap_init(&context.entities);
+
+    // NOTE: Add builtin entities to pipeline.
+    entity_heap_insert(&context.entities, ((Entity) {
+        .state = Entity_State_Parse_Builtin,
+        .type = Entity_Type_Load_File,
+        .package = NULL,
+        .include = create_load(context.ast_alloc, "core/builtin"),
+    }));
+
+    entity_heap_insert(&context.entities, ((Entity) {
+        .state = Entity_State_Parse_Builtin,
+        .type = Entity_Type_Load_File,
+        .package = NULL,
+        .include = create_load(context.ast_alloc, "core/runtime/build_opts"),
+    }));
+
+    if (context.options->runtime != Runtime_Custom) {
+        runtime_info_types_entity = entity_heap_insert(&context.entities, ((Entity) {
+            .state = Entity_State_Parse,
+            .type = Entity_Type_Load_File,
+            .package = NULL,
+            .include = create_load(context.ast_alloc, "core/runtime/info/types"),
+        }));
+        runtime_info_foreign_entity = entity_heap_insert(&context.entities, ((Entity) {
+            .state = Entity_State_Parse,
+            .type = Entity_Type_Load_File,
+            .package = NULL,
+            .include = create_load(context.ast_alloc, "core/runtime/info/foreign_blocks"),
+        }));
+        runtime_info_proc_tags_entity = entity_heap_insert(&context.entities, ((Entity) {
+            .state = Entity_State_Parse,
+            .type = Entity_Type_Load_File,
+            .package = NULL,
+            .include = create_load(context.ast_alloc, "core/runtime/info/proc_tags"),
+        }));
+    }
+
+    add_entities_for_node(NULL, (AstNode *) &builtin_stack_top, context.global_scope, NULL);
+    add_entities_for_node(NULL, (AstNode *) &builtin_heap_start, context.global_scope, NULL);
+    add_entities_for_node(NULL, (AstNode *) &builtin_tls_base, context.global_scope, NULL);
+    add_entities_for_node(NULL, (AstNode *) &builtin_tls_size, context.global_scope, NULL);
+
+    // NOTE: Add all files passed by command line to the queue
+    bh_arr_each(const char *, filename, opts->files) {
+        AstInclude* load_node = create_load(context.ast_alloc, (char *) *filename);
+        add_entities_for_node(NULL, (AstNode *) load_node, context.global_scope, NULL);
+    }
+}
+
+static void context_free() {
+    bh_arena_free(&context.ast_arena);
+    bh_arr_free(context.loaded_files);
+
+    compile_opts_free(context.options);
+}
+
+static void parse_source_file(bh_file_contents* file_contents) {
+    // :Remove passing the allocators as parameters
+    OnyxTokenizer tokenizer = onyx_tokenizer_create(context.token_alloc, file_contents);
+    onyx_lex_tokens(&tokenizer);
+
+    file_contents->line_count = tokenizer.line_number;
+
+    OnyxParser parser = onyx_parser_create(context.ast_alloc, &tokenizer);
+    onyx_parse(&parser);
+    onyx_parser_free(&parser);
+}
+
+static b32 process_source_file(char* filename, OnyxFilePos error_pos) {
+    bh_arr_each(bh_file_contents, fc, context.loaded_files) {
+        // Duplicates are detected here and since these filenames will be the full path,
+        // string comparing them should be all that is necessary.
+        if (!strcmp(fc->filename, filename)) return 1;
+    }
+
+    bh_file file;
+    bh_file_error err = bh_file_open(&file, filename);
+    if (err != BH_FILE_ERROR_NONE) {
+        if (context.cycle_detected) {
+            onyx_report_error(error_pos, Error_Critical, "Failed to open file %s", filename);
+        }
+        return 0;
+    }
+
+    bh_file_contents fc = bh_file_read_contents(context.token_alloc, &file);
+    bh_file_close(&file);
+
+    bh_arr_push(context.loaded_files, fc);
+
+    if (context.options->verbose_output == 2)
+        bh_printf("Processing source file:    %s (%d bytes)\n", file.filename, fc.length);
+
+    parse_source_file(&bh_arr_last(context.loaded_files));
+    return 1;
+}
+
+static b32 process_load_entity(Entity* ent) {
+    assert(ent->type == Entity_Type_Load_File || ent->type == Entity_Type_Load_Path);
+    AstInclude* include = ent->include;
+
+    if (include->kind == Ast_Kind_Load_File) {
+        // :RelativeFiles
+        const char* parent_file = include->token->pos.filename;
+        if (parent_file == NULL) parent_file = ".";
+
+        char* parent_folder = bh_path_get_parent(parent_file, global_scratch_allocator);
+
+        char* filename = bh_lookup_file(include->name, parent_folder, ".onyx", 1, context.options->included_folders, 1);
+        char* formatted_name = bh_strdup(global_heap_allocator, filename);
+
+        return process_source_file(formatted_name, include->token->pos);
+
+    } else if (include->kind == Ast_Kind_Load_All) {
+        const char* parent_file = include->token->pos.filename;
+        if (parent_file == NULL) parent_file = ".";
+
+        char* parent_folder = bh_path_get_parent(parent_file, global_scratch_allocator);
+        char folder[512];
+        if (bh_str_starts_with(include->name, "./")) {
+            bh_snprintf(folder, 511, "%s/%s", parent_folder, include->name + 2);
+        } else {
+            bh_snprintf(folder, 511, "%s", include->name);
+        }
+
+        // This does not take into account #load_path'd folders...
+
+        bh_path_convert_separators(folder);
+        bh_dir dir = bh_dir_open(folder);
+        if (dir == NULL) {
+            onyx_report_error(include->token->pos, Error_Critical, "Could not find folder '%s'.", folder);
+            return 0;
+        }
+
+        bh_dirent entry;
+        b32 success = 1;
+        char fullpath[512];
+        while (bh_dir_read(dir, &entry)) {
+            if (entry.type == BH_DIRENT_FILE && bh_str_ends_with(entry.name, ".onyx")) {
+                bh_snprintf(fullpath, 511, "%s/%s", folder, entry.name);
+                bh_path_convert_separators(fullpath);
+                u8* formatted_name = bh_path_get_full_name(fullpath, global_heap_allocator);
+                success = process_source_file(formatted_name, include->token->pos);
+                if (!success) break;
+            }
+        }
+
+        bh_dir_close(dir);
+        return success;
+
+    } else if (include->kind == Ast_Kind_Load_Path) {
+        bh_arr_push(context.options->included_folders, include->name);
+
+    } else if (include->kind == Ast_Kind_Library_Path) {
+        bh_arr_push(context.wasm_module->library_paths, include->name);
+    }
+
+    return 1;
+}
+
+static b32 process_entity(Entity* ent) {
+    static char verbose_output_buffer[512];
+    if (context.options->verbose_output == 3) {
+        if (ent->expr && ent->expr->token)
+            snprintf(verbose_output_buffer, 511,
+                    "%20s | %24s (%d, %d) | %s:%i:%i \n",
+                   entity_state_strings[ent->state],
+                   entity_type_strings[ent->type],
+                   (u32) ent->macro_attempts,
+                   (u32) ent->micro_attempts,
+                   ent->expr->token->pos.filename,
+                   ent->expr->token->pos.line,
+                   ent->expr->token->pos.column);
+
+        else if (ent->expr)
+            snprintf(verbose_output_buffer, 511,
+                    "%20s | %24s (%d, %d) \n",
+                   entity_state_strings[ent->state],
+                   entity_type_strings[ent->type],
+                   (u32) ent->macro_attempts,
+                   (u32) ent->micro_attempts);
+    }
+
+    // CLEANUP: There should be a nicer way to track if the builtins have
+    // already been initialized.
+    static b32 builtins_initialized = 0;
+
+    EntityState before_state = ent->state;
+    switch (before_state) {
+        case Entity_State_Error:
+            if (ent->type != Entity_Type_Error) {
+                onyx_report_error(ent->expr->token->pos, Error_Critical, "Error entity unexpected. This is definitely a compiler bug");
+            } else {
+                onyx_report_error(ent->error->token->pos, Error_Critical, "Static error occured: '%b'", ent->error->error_msg->text, ent->error->error_msg->length);
+            }
+            break;
+
+        case Entity_State_Parse_Builtin:
+            process_load_entity(ent);
+            ent->state = Entity_State_Finalized;
+            break;
+
+        case Entity_State_Introduce_Symbols:
+            // Currently, introducing symbols is handled in the symbol resolution
+            // function. Maybe there should be a different place where that happens?
+            symres_entity(ent);
+            break;
+
+        case Entity_State_Parse:
+            if (!builtins_initialized) {
+                builtins_initialized = 1;
+                initialize_builtins(context.ast_alloc);
+                introduce_build_options(context.ast_alloc);
+            }
+
+            // GROSS
+            if (special_global_entities_remaining == 0) {
+                special_global_entities_remaining--;
+                initalize_special_globals();
+            }
+
+            if (process_load_entity(ent)) {
+                // GROSS
+                if (ent == runtime_info_types_entity
+                    || ent == runtime_info_proc_tags_entity
+                    || ent == runtime_info_foreign_entity) {
+                    special_global_entities_remaining--;
+                }
+
+                ent->state = Entity_State_Finalized;
+            } else {
+                ent->macro_attempts++;
+            }
+            break;
+
+        case Entity_State_Resolve_Symbols: symres_entity(ent); break;
+        case Entity_State_Check_Types:     check_entity(ent);  break;
+        case Entity_State_Code_Gen:        emit_entity(ent);   break;
+    }
+
+    b32 changed = ent->state != before_state;
+    if (context.options->verbose_output == 3) {
+        if (changed) printf("SUCCESS to %20s | %s", entity_state_strings[ent->state], verbose_output_buffer);
+        else         printf("YIELD   to %20s | %s", entity_state_strings[ent->state], verbose_output_buffer);
+    }
+
+    return changed;
+}
+
+// Just having fun with some visual output - brendanfh 2020/12/14
+#if defined(_BH_LINUX)
+static void output_dummy_progress_bar() {
+    EntityHeap* eh = &context.entities;
+    if (bh_arr_length(eh->entities) == 0) return;
+
+    static const char* state_colors[] = {
+        "\e[91m", "\e[93m", "\e[94m", "\e[93m",
+        "\e[97m", "\e[95m", "\e[96m", "\e[92m", "\e[91m",
+    };
+
+    printf("\e[2;1H");
+
+    for (i32 i = 0; i < Entity_State_Count - 1; i++) {
+        if (i % 2 == 0) printf("\n");
+        printf("%s %25s \xe2\x96\x88 ", state_colors[i], entity_state_strings[i]);
+    }
+
+    printf("\n\n");
+
+    for (i32 i = 0; i < Entity_Type_Count; i++) {
+        if      (eh->type_count[i] == 0)           printf("\e[90m");
+        else if ((i32) eh->entities[0]->type == i) printf("\e[92m");
+        else                                       printf("\e[97m");
+
+        printf("%25s (%4d) | ", entity_type_strings[i], eh->type_count[i]);
+
+        printf("\e[0K");
+        for (i32 j = 0; j < Entity_State_Count; j++) {
+            if (eh->all_count[j][i] == 0) continue;
+
+            printf("%s", state_colors[j]);
+
+            i32 count = (eh->all_count[j][i] >> 5) + 1;
+            for (i32 c = 0; c < count * 2; c++) printf("\xe2\x96\x88");
+
+            printf("\e[0m");
+        }
+        printf("\n");
+    }
+}
+#endif
+
+static void dump_cycles() {
+    context.cycle_detected = 1;
+    Entity* ent;
+
+    while (1) {
+        ent = entity_heap_top(&context.entities);
+        entity_heap_remove_top(&context.entities);
+        if (ent->state < Entity_State_Code_Gen) process_entity(ent);
+        else break;
+
+        if (bh_arr_length(context.entities.entities) == 0) {
+            break;
+        }
+    }
+}
+
+static i32 onyx_compile() {
+    u64 start_time = bh_time_curr();
+
+    if (context.options->fun_output)
+        printf("\e[2J");
+
+    while (!bh_arr_is_empty(context.entities.entities)) {
+        Entity* ent = entity_heap_top(&context.entities);
+
+#if defined(_BH_LINUX)
+        if (context.options->fun_output) {
+            output_dummy_progress_bar();
+
+            if (ent->expr->token) {
+                OnyxFilePos pos = ent->expr->token->pos;
+                printf("\e[0K%s on %s in %s:%d:%d\n", entity_state_strings[ent->state], entity_type_strings[ent->type], pos.filename, pos.line, pos.column);
+            }
+
+            // Slowing things down for the effect
+            usleep(1000);
+        }
+#endif
+
+        entity_heap_remove_top(&context.entities);
+        b32 changed = process_entity(ent);
+
+        // NOTE: VERY VERY dumb cycle breaking. Basically, remember the first entity that did
+        // not change (i.e. did not make any progress). Then everytime an entity doesn't change,
+        // check if it is the same entity. If it is, it means all other entities that were processed
+        // between the two occurences didn't make any progress either, and there must be a cycle.
+        //                                                              - brendanfh 2021/02/06
+        //
+        // Because of the recent changes to the compiler architecture (again), this condition
+        // does not always hold anymore. There can be nodes that get scheduled multiple times
+        // before the "key" node that will unblock the progress. This means a more sophisticated
+        // cycle detection algorithm must be used.
+        //
+        static Entity* watermarked_node = NULL;
+        static u32 highest_watermark = 0;
+        if (!changed) {
+            if (!watermarked_node) {
+                watermarked_node = ent;
+                highest_watermark = bh_max(highest_watermark, ent->macro_attempts);
+            }
+            else if (watermarked_node == ent) {
+                if (ent->macro_attempts > highest_watermark) {
+                    entity_heap_insert_existing(&context.entities, ent);
+
+                    if (context.cycle_almost_detected) {
+                        dump_cycles();
+                    } else {
+                        context.cycle_almost_detected = 1;
+                    }
+                }
+            }
+            else if (watermarked_node->macro_attempts < ent->macro_attempts) {
+                watermarked_node = ent;
+                highest_watermark = bh_max(highest_watermark, ent->macro_attempts);
+            }
+        } else {
+            watermarked_node = NULL;
+            context.cycle_almost_detected = 0;
+        }
+
+        if (onyx_has_errors()) return ONYX_COMPILER_PROGRESS_ERROR;
+
+        if (ent->state != Entity_State_Finalized && ent->state != Entity_State_Failed)
+            entity_heap_insert_existing(&context.entities, ent);
+    }
+
+    u64 duration = bh_time_duration(start_time);
+
+    if (context.options->verbose_output > 0) {
+        // TODO: Replace these with bh_printf when padded formatting is added.
+        printf("\nStatistics:\n");
+        printf("    Time taken: %lf seconds\n", (double) duration / 1000);
+        printf("    Processed %ld lines (%f lines/second).\n", lexer_lines_processed, ((f32) 1000 * lexer_lines_processed) / (duration));
+        printf("    Processed %ld tokens (%f tokens/second).\n", lexer_tokens_processed, ((f32) 1000 * lexer_tokens_processed) / (duration));
+        printf("\n");
+    }
+
+    return ONYX_COMPILER_PROGRESS_SUCCESS;
+}
+
+static void link_wasm_module() {
+    Package *runtime_var_package = package_lookup("runtime.vars");
+    assert(runtime_var_package);
+
+    AstTyped *link_options_node = (AstTyped *) symbol_raw_resolve(runtime_var_package->scope, "link_options");
+    Type *link_options_type = type_build_from_ast(context.ast_alloc, builtin_link_options_type);
+
+    assert(unify_node_and_type(&link_options_node, link_options_type) == TYPE_MATCH_SUCCESS);
+
+    OnyxWasmLinkOptions link_opts;
+    // CLEANUP: Properly handle this case.
+    assert(onyx_wasm_build_link_options_from_node(&link_opts, link_options_node));
+
+    onyx_wasm_module_link(context.wasm_module, &link_opts);
+}
+
+static CompilerProgress onyx_flush_module() {
+    link_wasm_module();
+
+    // NOTE: Output to file
+    bh_file output_file;
+    if (bh_file_create(&output_file, context.options->target_file) != BH_FILE_ERROR_NONE)
+        return ONYX_COMPILER_PROGRESS_FAILED_OUTPUT;
+
+    if (context.options->verbose_output)
+        bh_printf("Outputting to WASM file:   %s\n", output_file.filename);
+
+    // APPARENTLY... the WebAssembly Threading proposal says that the data segment initializations
+    // in a WASM module are copied into the linear memory EVERY time the module is instantiated, not
+    // just the first time. This means that if we are happily chugging along and modifying global state
+    // and then we spawn a thread, that thread will completely wipe all changes to the global and return
+    // it to its original state. This is horrible obviously, but the only thing that is more horrible is
+    // that the best way around this is to create a second WASM module that simply initializes the given
+    // data section. Then have a section module that is actually your code. For right now, this is going
+    // to be fine since the browser is really the only place that multi-threading can be used to any
+    // degree of competency. But still... This is god awful and I hope that there is some other way to
+    // around this down the line.
+    if (context.options->use_multi_threading && !context.options->use_post_mvp_features) {
+        bh_file data_file;
+        if (bh_file_create(&data_file, bh_aprintf(global_scratch_allocator, "%s.data", context.options->target_file)) != BH_FILE_ERROR_NONE)
+            return ONYX_COMPILER_PROGRESS_FAILED_OUTPUT;
+
+        OnyxWasmModule* data_module = bh_alloc_item(global_heap_allocator, OnyxWasmModule);
+        *data_module = onyx_wasm_module_create(global_heap_allocator);
+
+        data_module->data = context.wasm_module->data;
+        context.wasm_module->data = NULL;
+
+        onyx_wasm_module_write_to_file(data_module, data_file);
+        onyx_wasm_module_write_to_file(context.wasm_module, output_file);
+
+        bh_file_close(&data_file);
+    } else {
+        onyx_wasm_module_write_to_file(context.wasm_module, output_file);
+    }
+
+    bh_file_close(&output_file);
+
+    if (context.options->documentation_file != NULL) {
+        OnyxDocumentation docs = onyx_docs_generate();
+        docs.format = Doc_Format_Human;
+        onyx_docs_emit(&docs, context.options->documentation_file);
+    }
+
+    return ONYX_COMPILER_PROGRESS_SUCCESS;
+}
+
+#ifdef ENABLE_RUN_WITH_WASMER
+static b32 onyx_run() {
+    link_wasm_module();
+
+    bh_buffer code_buffer;
+    onyx_wasm_module_write_to_buffer(context.wasm_module, &code_buffer);
+
+    onyx_run_initialize(context.options->debug_enabled);
+
+    if (context.options->verbose_output > 0)
+        bh_printf("Running program:\n");
+
+    return onyx_run_wasm(code_buffer, context.options->passthrough_argument_count, context.options->passthrough_argument_data);
+}
+#endif
+
+int main(int argc, char *argv[]) {
+
+    bh_scratch_init(&global_scratch, bh_heap_allocator(), 256 * 1024); // NOTE: 256 KiB
+    global_scratch_allocator = bh_scratch_allocator(&global_scratch);
+
+    // SPEED: This used to be a managed heap allocator where all allocations
+    // were tracked and would be automatically freed at the end of execution.
+    // I don't know why I ever did that because that is the job of the operating
+    // system when a process exits.
+    global_heap_allocator = bh_heap_allocator();
+
+    CompileOptions compile_opts = compile_opts_parse(global_heap_allocator, argc, argv);
+    context_init(&compile_opts);
+
+    CompilerProgress compiler_progress = ONYX_COMPILER_PROGRESS_ERROR;
+    switch (compile_opts.action) {
+        case ONYX_COMPILE_ACTION_PRINT_HELP: bh_printf(docstring); return 1;
+
+        case ONYX_COMPILE_ACTION_COMPILE:
+            compiler_progress = onyx_compile();
+            if (compiler_progress == ONYX_COMPILER_PROGRESS_SUCCESS) {
+                onyx_flush_module();
+            }
+            break;
+
+        #ifdef ENABLE_RUN_WITH_WASMER
+        case ONYX_COMPILE_ACTION_RUN:
+            compiler_progress = onyx_compile();
+            if (compiler_progress == ONYX_COMPILER_PROGRESS_SUCCESS) {
+                if (!onyx_run()) {
+                    compiler_progress = ONYX_COMPILER_PROGRESS_ERROR;
+                }
+            }
+            break;
+        #endif
+
+        default: break;
+    }
+
+    switch (compiler_progress) {
+        case ONYX_COMPILER_PROGRESS_ERROR:
+            onyx_errors_print();
+            break;
+
+        case ONYX_COMPILER_PROGRESS_FAILED_OUTPUT:
+            bh_printf_err("Failed to open file for writing: '%s'\n", compile_opts.target_file);
+            break;
+
+        case ONYX_COMPILER_PROGRESS_SUCCESS:
+            break;
+    }
+
+    context_free();
+
+    bh_scratch_free(&global_scratch);
+    // bh_managed_heap_free(&global_heap);
+
+    return compiler_progress != ONYX_COMPILER_PROGRESS_SUCCESS;
+}
diff --git a/compiler/src/onyxrun.c b/compiler/src/onyxrun.c
new file mode 100644 (file)
index 0000000..5200090
--- /dev/null
@@ -0,0 +1,42 @@
+#define BH_DEFINE
+#define BH_NO_TABLE
+#define STB_DS_IMPLEMENTATION
+#include "bh.h"
+
+#include "wasm_emit.h"
+
+int main(int argc, char *argv[]) {
+    i32 wasm_file_idx = 1;
+    if (argc < 2) {
+        fprintf(stderr, "Expected a WASM file to run.\n");
+        return 1;
+    }
+
+    b32 debug = 0;
+    if (!strcmp(argv[1], "--debug")) {
+        debug = 1;
+        wasm_file_idx = 2;
+    }
+
+    if (debug && argc < 3) {
+        fprintf(stderr, "Expected a WASM file to run.\n");
+        return 1;
+    }
+
+    onyx_run_initialize(debug);
+
+    bh_file wasm_file;
+    bh_file_error err = bh_file_open(&wasm_file, argv[wasm_file_idx]);
+    if (err != BH_FILE_ERROR_NONE) {
+        fprintf(stderr, "Failed to open file %s", argv[wasm_file_idx]);
+        return 1;
+    }
+
+    bh_file_contents wasm_data = bh_file_read_contents(bh_heap_allocator(), &wasm_file);
+    bh_file_close(&wasm_file);
+
+    bh_buffer data;
+    data.data = wasm_data.data;
+    data.length = wasm_data.length;
+    return onyx_run_wasm(data, argc - 1 - debug, argv + 1 + debug);
+}
diff --git a/compiler/src/parser.c b/compiler/src/parser.c
new file mode 100644 (file)
index 0000000..1ccf752
--- /dev/null
@@ -0,0 +1,3475 @@
+// The sole job of the parser for Onyx is to submit nodes to the
+// entity heap for further processing. These nodes include things
+// such as procedure definitions, string literals, struct definitions
+// and declarations to be introduced into scopes.
+
+#include "lex.h"
+#include "errors.h"
+#include "parser.h"
+#include "utils.h"
+
+#define make_node(nclass, kind)             onyx_ast_node_new(parser->allocator, sizeof(nclass), kind)
+// :LinearTokenDependent
+#define peek_token(ahead)                   (parser->curr + ahead)
+
+static AstNode error_node = { Ast_Kind_Error, 0, NULL, NULL };
+
+#define ENTITY_SUBMIT(node)                 (submit_entity_in_scope(parser, (AstNode *) (node), parser->current_scope, parser->package))
+#define ENTITY_SUBMIT_IN_SCOPE(node, scope) (submit_entity_in_scope(parser, (AstNode *) (node), scope, parser->package))
+
+void submit_entity_in_scope(OnyxParser* parser, AstNode* node, Scope* scope, Package* package) {
+    if (bh_arr_length(parser->alternate_entity_placement_stack) == 0) {
+        add_entities_for_node(NULL, node, scope, package);
+
+    } else {
+        bh_arr(Entity *) *entity_array = bh_arr_last(parser->alternate_entity_placement_stack);
+        add_entities_for_node(entity_array, node, scope, package);
+    }
+}
+
+// Parsing Utilities
+static void consume_token(OnyxParser* parser);
+static OnyxToken* expect_token(OnyxParser* parser, TokenType token_type);
+static b32 consume_token_if_next(OnyxParser* parser, TokenType token_type);
+static b32 next_tokens_are(OnyxParser* parser, i32 n, ...);
+static OnyxToken* find_matching_paren(OnyxToken* paren);
+
+static AstNumLit*     parse_int_literal(OnyxParser* parser);
+static AstNumLit*     parse_float_literal(OnyxParser* parser);
+static b32            parse_possible_struct_literal(OnyxParser* parser, AstTyped* left, AstTyped** ret);
+static b32            parse_possible_array_literal(OnyxParser* parser, AstTyped* left, AstTyped** ret);
+static b32            parse_possible_unary_field_access(OnyxParser* parser, AstTyped** ret);
+static void           parse_arguments(OnyxParser* parser, TokenType end_token, Arguments* args);
+static AstTyped*      parse_factor(OnyxParser* parser);
+static AstTyped*      parse_compound_assignment(OnyxParser* parser, AstTyped* lhs);
+static AstTyped*      parse_compound_expression(OnyxParser* parser, b32 assignment_allowed);
+static AstTyped*      parse_expression(OnyxParser* parser, b32 assignment_allowed);
+static AstIfWhile*    parse_if_stmt(OnyxParser* parser);
+static AstIfWhile*    parse_while_stmt(OnyxParser* parser);
+static AstFor*        parse_for_stmt(OnyxParser* parser);
+static AstSwitchCase* parse_case_stmt(OnyxParser* parser);
+static AstSwitch*     parse_switch_stmt(OnyxParser* parser);
+static i32            parse_possible_symbol_declaration(OnyxParser* parser, AstNode** ret);
+static AstReturn*     parse_return_stmt(OnyxParser* parser);
+static AstNode*       parse_use_stmt(OnyxParser* parser);
+static AstBlock*      parse_block(OnyxParser* parser, b32 make_a_new_scope, char* block_name);
+static AstNode*       parse_statement(OnyxParser* parser);
+static void           parse_polymorphic_variable(OnyxParser* parser, AstType*** next_insertion);
+static AstType*       parse_type(OnyxParser* parser);
+static AstTypeOf*     parse_typeof(OnyxParser* parser);
+static AstStructType* parse_struct(OnyxParser* parser);
+static AstInterface*  parse_interface(OnyxParser* parser);
+static AstConstraint* parse_constraint(OnyxParser* parser);
+static void           parse_constraints(OnyxParser* parser, ConstraintContext *constraints);
+static void           parse_function_params(OnyxParser* parser, AstFunction* func);
+static b32            parse_possible_directive(OnyxParser* parser, const char* dir);
+static b32            parse_possible_function_definition_no_consume(OnyxParser* parser);
+static b32            parse_possible_function_definition(OnyxParser* parser, AstTyped** ret);
+static b32            parse_possible_quick_function_definition_no_consume(OnyxParser* parser);
+static b32            parse_possible_quick_function_definition(OnyxParser* parser, AstTyped** ret);
+static AstFunction*   parse_function_definition(OnyxParser* parser, OnyxToken* token);
+static AstTyped*      parse_global_declaration(OnyxParser* parser);
+static AstEnumType*   parse_enum_declaration(OnyxParser* parser);
+static AstMacro*      parse_macro(OnyxParser* parser);
+static AstIf*         parse_static_if_stmt(OnyxParser* parser, b32 parse_block_as_statements);
+static AstMemRes*     parse_memory_reservation(OnyxParser* parser, OnyxToken* symbol, b32 thread_local);
+static AstTyped*      parse_top_level_expression(OnyxParser* parser);
+static AstBinding*    parse_top_level_binding(OnyxParser* parser, OnyxToken* symbol);
+static void           parse_top_level_statement(OnyxParser* parser);
+static AstPackage*    parse_package_expression(OnyxParser* parser);
+static void           parse_top_level_statements_until(OnyxParser* parser, TokenType tt);
+
+static void consume_token(OnyxParser* parser) {
+    if (parser->hit_unexpected_token) return;
+
+    parser->prev = parser->curr;
+    // :LinearTokenDependent
+    parser->curr++;
+    while (parser->curr->type == Token_Type_Comment || parser->curr->type == Token_Type_Note) {
+        if (parser->curr->type == Token_Type_Note) {
+            AstNote* note = make_node(AstNode, Ast_Kind_Note);
+            note->token = parser->curr;
+            ENTITY_SUBMIT(note);
+        }
+
+        parser->curr++;
+    }
+}
+
+static OnyxToken* find_matching_paren(OnyxToken* paren) {
+    TokenType match = 0;
+    switch ((u16) paren->type) {
+        case '(': match = ')'; break;
+        case '[': match = ']'; break;
+        case '{': match = '}'; break;
+        case '<': match = '>'; break;
+        default: return NULL;
+    }
+
+    i32 paren_count = 1;
+    i32 i = 1;
+    while (paren_count > 0) {
+        // :LinearTokenDependent
+        TokenType type = (paren + i)->type;
+        if (type == Token_Type_End_Stream) return NULL;
+
+        if (type == paren->type) paren_count++;
+        else if (type == match)  paren_count--;
+
+        i++;
+    }
+
+    // :LinearTokenDependent
+    return paren + (i - 1);
+}
+
+// NOTE: This advances to next token no matter what
+static OnyxToken* expect_token(OnyxParser* parser, TokenType token_type) {
+    if (parser->hit_unexpected_token) return NULL;
+
+    OnyxToken* token = parser->curr;
+    consume_token(parser);
+
+    if (token->type != token_type) {
+        onyx_report_error(token->pos, Error_Critical, "expected token '%s', got '%s'.", token_name(token_type), token_name(token->type));
+        parser->hit_unexpected_token = 1;
+        // :LinearTokenDependent
+        parser->curr = &parser->tokenizer->tokens[bh_arr_length(parser->tokenizer->tokens) - 1];
+        return NULL;
+    }
+
+    return token;
+}
+
+static b32 consume_token_if_next(OnyxParser* parser, TokenType token_type) {
+    if (parser->hit_unexpected_token) return 0;
+
+    if (parser->curr->type == token_type) {
+        consume_token(parser);
+        return 1;
+    } else {
+        return 0;
+    }
+}
+
+static void consume_tokens(OnyxParser* parser, i32 n) {
+    fori (i, 0, n) consume_token(parser);
+}
+
+static b32 next_tokens_are(OnyxParser* parser, i32 n, ...) {
+    va_list va;
+    va_start(va, n);
+
+    i32 matched = 1;
+
+    // BUG: This does not take into consideration comments and notes that can occur between any tokens.
+    fori (i, 0, n) {
+        TokenType expected_type = va_arg(va, TokenType);
+        if (peek_token(i)->type != expected_type) {
+            matched = 0;
+            break;
+        }
+    }
+
+    va_end(va);
+    return matched;
+}
+
+
+static void expect_no_stored_tags_pos(OnyxParser *parser, OnyxFilePos pos) {
+    if (bh_arr_length(parser->stored_tags) > 0) {
+        onyx_report_error(pos, Error_Critical, "#tag is not allowed on this element.");
+        parser->hit_unexpected_token = 1;
+    }
+}
+
+static void expect_no_stored_tags(OnyxParser *parser) {
+    expect_no_stored_tags_pos(parser, parser->curr->pos);
+}
+
+static void flush_stored_tags(OnyxParser *parser, bh_arr(AstTyped *) *out_arr) {
+    //
+    // When tag_depth > 0, no tags will be added to the element.
+    // This happens if you have a something like so,
+    //
+    // #tag "asdf"
+    // #tag handler.{ (x: i32) => x * 2 }
+    // foo :: () { ... }
+    //
+    // In this situation, the inner procedure defined in the second
+    // tag should NOT consume the "asdf" tag.
+    //
+    if (bh_arr_length(parser->stored_tags) == 0 || parser->tag_depth > 0) return;
+
+    bh_arr(AstTyped *) arr = *out_arr;
+
+    if (arr == NULL) {
+        bh_arr_new(parser->allocator, arr, bh_arr_length(parser->stored_tags));
+    }
+
+    bh_arr_each(AstTyped *, pexpr, parser->stored_tags) {
+        bh_arr_push(arr, *pexpr);
+    }
+
+    bh_arr_clear(parser->stored_tags);
+
+    *out_arr = arr;
+}
+
+
+// TODO: Make parsing numeric literals not rely on the C standard libary.
+static AstNumLit* parse_int_literal(OnyxParser* parser) {
+    AstNumLit* int_node = make_node(AstNumLit, Ast_Kind_NumLit);
+    int_node->token = expect_token(parser, Token_Type_Literal_Integer);
+    int_node->flags |= Ast_Flag_Comptime;
+    int_node->value.l = 0ll;
+
+    token_toggle_end(int_node->token);
+
+    char* first_invalid = NULL;
+    i64 value = strtoll(int_node->token->text, &first_invalid, 0);
+
+    int_node->value.l = value;
+
+    int_node->type_node = (AstType *) &basic_type_int_unsized;
+
+    // NOTE: Hex literals are unsigned.
+    if (int_node->token->length >= 2 && int_node->token->text[1] == 'x') {
+        int_node->was_hex_literal = 1;
+    }
+
+    token_toggle_end(int_node->token);
+    return int_node;
+}
+
+static AstNumLit* parse_float_literal(OnyxParser* parser) {
+    AstNumLit* float_node = make_node(AstNumLit, Ast_Kind_NumLit);
+    float_node->token = expect_token(parser, Token_Type_Literal_Float);
+    float_node->flags |= Ast_Flag_Comptime;
+    float_node->value.d = 0.0;
+
+    AstType* type = (AstType *) &basic_type_float_unsized;
+    token_toggle_end(float_node->token);
+
+    if (float_node->token->text[float_node->token->length - 1] == 'f') {
+        type = (AstType *) &basic_type_f32;
+        float_node->value.f = strtof(float_node->token->text, NULL);
+    } else {
+        float_node->value.d = strtod(float_node->token->text, NULL);
+    }
+
+    float_node->type_node = type;
+
+    token_toggle_end(float_node->token);
+    return float_node;
+}
+
+static b32 parse_possible_directive(OnyxParser* parser, const char* dir) {
+    if (!next_tokens_are(parser, 2, '#', Token_Type_Symbol)) return 0;
+
+    OnyxToken* sym = peek_token(1);
+
+    b32 match = (strlen(dir) == (u64) sym->length) && (strncmp(dir, sym->text, sym->length) == 0);
+    if (match) consume_tokens(parser, 2);
+
+    return match;
+}
+
+static b32 parse_possible_struct_literal(OnyxParser* parser, AstTyped* left, AstTyped** ret) {
+    if (!next_tokens_are(parser, 2, '.', '{')) return 0;
+
+    AstStructLiteral* sl = make_node(AstStructLiteral, Ast_Kind_Struct_Literal);
+    sl->token = parser->curr;
+    sl->stnode = left;
+
+    arguments_initialize(&sl->args);
+
+    expect_token(parser, '.');
+    expect_token(parser, '{');
+
+    parse_arguments(parser, '}', &sl->args);
+
+    *ret = (AstTyped *) sl;
+    return 1;
+}
+
+static b32 parse_possible_array_literal(OnyxParser* parser, AstTyped* left, AstTyped** ret) {
+    if (!next_tokens_are(parser, 2, '.', '[')) return 0;
+
+    AstArrayLiteral* al = make_node(AstArrayLiteral, Ast_Kind_Array_Literal);
+    al->token = parser->curr;
+    al->atnode = left;
+
+    bh_arr_new(global_heap_allocator, al->values, 4);
+    fori (i, 0, 4) al->values[i] = NULL;
+
+    expect_token(parser, '.');
+    expect_token(parser, '[');
+    while (!consume_token_if_next(parser, ']')) {
+        if (parser->hit_unexpected_token) return 1;
+
+        AstTyped* value = parse_expression(parser, 0);
+        bh_arr_push(al->values, value);
+
+        if (parser->curr->type != ']')
+            expect_token(parser, ',');
+    }
+
+    *ret = (AstTyped *) al;
+    return 1;
+}
+
+static b32 parse_possible_unary_field_access(OnyxParser* parser, AstTyped** ret) {
+    if (!next_tokens_are(parser, 2, '.', Token_Type_Symbol)) return 0;
+
+    AstUnaryFieldAccess* ufl = make_node(AstUnaryFieldAccess, Ast_Kind_Unary_Field_Access);
+    expect_token(parser, '.');
+    ufl->token = expect_token(parser, Token_Type_Symbol);
+
+    *ret = (AstTyped *) ufl;
+    return 1;
+}
+
+static void parse_arguments(OnyxParser* parser, TokenType end_token, Arguments* args) {
+    while (!consume_token_if_next(parser, end_token)) {
+        if (parser->hit_unexpected_token) return;
+
+        //
+        // This has a weird condition to avoid the problem of using a quick function as an argument:
+        //     f(x => x + 1)
+        // This shouldn't be a named argument, but this should:
+        //     f(g = x => x + 1)
+        //
+        if (next_tokens_are(parser, 2, Token_Type_Symbol, '=') && peek_token(2)->type != '>') {
+            OnyxToken* name = expect_token(parser, Token_Type_Symbol);
+            expect_token(parser, '=');
+
+            AstNamedValue* named_value = make_node(AstNamedValue, Ast_Kind_Named_Value);
+            named_value->token = name;
+            named_value->value = parse_expression(parser, 0);
+
+            bh_arr_push(args->named_values, named_value);
+
+        } else {
+            AstTyped* value = parse_expression(parser, 0);
+            bh_arr_push(args->values, value);
+        }
+
+        if (parser->curr->type != end_token)
+            expect_token(parser, ',');
+    }
+}
+
+static AstTyped* parse_factor(OnyxParser* parser) {
+    AstTyped* retval = NULL;
+
+    switch ((u16) parser->curr->type) {
+        case '(': {
+            if (parse_possible_function_definition(parser, &retval)) {
+                ENTITY_SUBMIT(retval);
+                break;
+            }
+            if (parse_possible_quick_function_definition(parser, &retval)) {
+                ENTITY_SUBMIT(retval);
+                break;
+            }
+
+            consume_token(parser);
+            retval = parse_compound_expression(parser, 0);
+            expect_token(parser, ')');
+            break;
+        }
+
+        case '-': {
+            AstUnaryOp* negate_node = make_node(AstUnaryOp, Ast_Kind_Unary_Op);
+            negate_node->operation = Unary_Op_Negate;
+            negate_node->token     = expect_token(parser, '-');
+            negate_node->expr      = parse_factor(parser);
+
+            retval = (AstTyped *) negate_node;
+            break;
+        }
+
+        case '!': {
+            AstUnaryOp* not_node = make_node(AstUnaryOp, Ast_Kind_Unary_Op);
+            not_node->operation = Unary_Op_Not;
+            not_node->token = expect_token(parser, '!');
+            not_node->expr = parse_factor(parser);
+
+            retval = (AstTyped *) not_node;
+            break;
+        }
+
+        case '~': {
+            AstUnaryOp* not_node = make_node(AstUnaryOp, Ast_Kind_Unary_Op);
+            not_node->operation = Unary_Op_Bitwise_Not;
+            not_node->token = expect_token(parser, '~');
+            not_node->expr = parse_factor(parser);
+
+            retval = (AstTyped *) not_node;
+            break;
+        }
+
+        case '*': {
+            AstDereference* deref_node = make_node(AstDereference, Ast_Kind_Dereference);
+            deref_node->token = expect_token(parser, '*');
+            deref_node->expr  = parse_factor(parser);
+
+            retval = (AstTyped *) deref_node;
+            break;
+        }
+
+        case '^': {
+            AstAddressOf* aof_node = make_node(AstAddressOf, Ast_Kind_Address_Of);
+            aof_node->token = expect_token(parser, '^');
+            aof_node->expr  = parse_factor(parser);
+
+            retval = (AstTyped *) aof_node;
+            break;
+        }
+
+        case '.': {
+            if (parse_possible_struct_literal(parser, NULL, &retval)) return retval;
+            if (parse_possible_array_literal(parser, NULL, &retval))  return retval;
+            if (parse_possible_unary_field_access(parser, &retval))   return retval;
+            goto no_match;
+        }
+
+        case Token_Type_Tilde_Tilde: {
+            AstUnaryOp* ac_node = make_node(AstUnaryOp, Ast_Kind_Unary_Op);
+            ac_node->operation = Unary_Op_Auto_Cast;
+            ac_node->token = expect_token(parser, Token_Type_Tilde_Tilde);
+            ac_node->expr = parse_factor(parser);
+
+            retval = (AstTyped *) ac_node;
+            break;
+        }
+
+        case Token_Type_Keyword_Cast: {
+            AstUnaryOp* cast_node = make_node(AstUnaryOp, Ast_Kind_Unary_Op);
+            cast_node->operation = Unary_Op_Cast;
+            cast_node->token = expect_token(parser, Token_Type_Keyword_Cast);
+
+            expect_token(parser, '(');
+            cast_node->type_node = parse_type(parser);
+            expect_token(parser, ')');
+
+            cast_node->expr = parse_factor(parser);
+
+            retval = (AstTyped *) cast_node;
+            break;
+        }
+
+        case Token_Type_Keyword_Sizeof: {
+            AstSizeOf* so_node = make_node(AstSizeOf, Ast_Kind_Size_Of);
+            so_node->token = expect_token(parser, Token_Type_Keyword_Sizeof);
+            so_node->so_ast_type = (AstType *) parse_type(parser);
+            so_node->type_node = (AstType *) &basic_type_i32;
+
+            retval = (AstTyped *) so_node;
+            break;
+        }
+
+        case Token_Type_Keyword_Alignof: {
+            AstAlignOf* ao_node = make_node(AstAlignOf, Ast_Kind_Align_Of);
+            ao_node->token = expect_token(parser, Token_Type_Keyword_Alignof);
+            ao_node->ao_ast_type = (AstType *) parse_type(parser);
+            ao_node->type_node = (AstType *) &basic_type_i32;
+
+            retval = (AstTyped *) ao_node;
+            break;
+        }
+
+        case Token_Type_Keyword_Typeof: {
+            retval = (AstTyped *) parse_typeof(parser);
+            break;
+        }
+
+        case Token_Type_Symbol: {
+            if (parse_possible_quick_function_definition(parser, &retval)) {
+                ENTITY_SUBMIT(retval);
+                break;
+            }
+
+            OnyxToken* sym_token = expect_token(parser, Token_Type_Symbol);
+            AstTyped* sym_node = make_node(AstTyped, Ast_Kind_Symbol);
+            sym_node->token = sym_token;
+
+            retval = sym_node;
+            break;
+        }
+
+        case Token_Type_Literal_Integer:
+            retval = (AstTyped *) parse_int_literal(parser);
+            break;
+
+        case Token_Type_Literal_Float:
+            retval = (AstTyped *) parse_float_literal(parser);
+            break;
+
+        case Token_Type_Literal_String: {
+            AstStrLit* str_node = make_node(AstStrLit, Ast_Kind_StrLit);
+            str_node->token     = expect_token(parser, Token_Type_Literal_String);
+            str_node->data_id   = 0;
+            str_node->flags    |= Ast_Flag_Comptime;
+
+            ENTITY_SUBMIT(str_node);
+
+            retval = (AstTyped *) str_node;
+            break;
+        }
+
+        case Token_Type_Literal_True: {
+            AstNumLit* bool_node = make_node(AstNumLit, Ast_Kind_NumLit);
+            bool_node->type_node = (AstType *) &basic_type_bool;
+            bool_node->token = expect_token(parser, Token_Type_Literal_True);
+            bool_node->value.i = 1;
+            bool_node->flags |= Ast_Flag_Comptime;
+            retval = (AstTyped *) bool_node;
+            break;
+        }
+
+        case Token_Type_Literal_False: {
+            AstNumLit* bool_node = make_node(AstNumLit, Ast_Kind_NumLit);
+            bool_node->type_node = (AstType *) &basic_type_bool;
+            bool_node->token = expect_token(parser, Token_Type_Literal_False);
+            bool_node->value.i = 0;
+            bool_node->flags |= Ast_Flag_Comptime;
+            retval = (AstTyped *) bool_node;
+            break;
+        }
+
+        case Token_Type_Keyword_Package: {
+            retval = (AstTyped *) parse_package_expression(parser);
+            break;
+        }
+
+        case Token_Type_Keyword_Macro: {
+            retval = (AstTyped *) parse_macro(parser);
+            break;
+        }
+
+        case Token_Type_Keyword_Do: {
+            OnyxToken* do_token = expect_token(parser, Token_Type_Keyword_Do);
+            AstDoBlock* do_block = make_node(AstDoBlock, Ast_Kind_Do_Block);
+            do_block->token = do_token;
+            do_block->type_node = (AstType *) &basic_type_auto_return;
+
+            if (parser->curr->type != '{') {
+                onyx_report_error(parser->curr->pos, Error_Critical, "Expected '{' after 'do', got '%s'.", token_name(parser->curr->type));
+                retval = NULL;
+                break;
+            }
+
+            do_block->block = parse_block(parser, 1, NULL);
+
+            retval = (AstTyped *) do_block;
+            break;
+        }
+
+        case '[': {
+            AstType *type = parse_type(parser);
+            retval = (AstTyped *) type;
+            break;
+        }
+
+        case '$': {
+            AstType **tmp = (AstType **) &retval;
+            parse_polymorphic_variable(parser, &tmp);
+            break;
+        }
+
+        case '#': {
+            if (parse_possible_directive(parser, "file_contents")) {
+                AstFileContents* fc = make_node(AstFileContents, Ast_Kind_File_Contents);
+                fc->token = parser->prev - 1;
+                fc->filename_expr = parse_expression(parser, 0);
+                fc->type = type_make_slice(parser->allocator, &basic_types[Basic_Kind_U8]);
+
+                if (parser->current_function_stack && bh_arr_length(parser->current_function_stack) > 0) {
+                    bh_arr_push(bh_arr_last(parser->current_function_stack)->nodes_that_need_entities_after_clone, (AstNode *) fc);
+                    
+                } else {
+                    ENTITY_SUBMIT(fc);
+                }
+
+                retval = (AstTyped *) fc;
+                break;
+            }
+            else if (parse_possible_directive(parser, "file")) {
+                OnyxToken* dir_token = parser->curr - 2;
+
+                OnyxToken* str_token = bh_alloc(parser->allocator, sizeof(OnyxToken));
+                str_token->text  = bh_strdup(global_heap_allocator, (char *) dir_token->pos.filename);
+                str_token->length = strlen(dir_token->pos.filename);
+                str_token->pos = dir_token->pos;
+                str_token->type = Token_Type_Literal_String;
+
+                AstStrLit* filename = make_node(AstStrLit, Ast_Kind_StrLit);
+                filename->token = str_token;
+                filename->data_id = 0;
+
+                ENTITY_SUBMIT(filename);
+                retval = (AstTyped *) filename;
+                break;
+            }
+            else if (parse_possible_directive(parser, "line")) {
+                OnyxToken* dir_token = parser->curr - 2;
+
+                AstNumLit* line_num = make_int_literal(parser->allocator, dir_token->pos.line);
+                retval = (AstTyped *) line_num;
+                break;
+            }
+            else if (parse_possible_directive(parser, "column")) {
+                OnyxToken* dir_token = parser->curr - 2;
+
+                AstNumLit* col_num = make_int_literal(parser->allocator, dir_token->pos.column);
+                retval = (AstTyped *) col_num;
+                break;
+            }
+            else if (parse_possible_directive(parser, "char")) {
+                AstNumLit* char_lit = make_node(AstNumLit, Ast_Kind_NumLit);
+                char_lit->flags |= Ast_Flag_Comptime;
+                char_lit->type_node = (AstType *) &basic_type_int_unsized;
+                char_lit->token = expect_token(parser, Token_Type_Literal_String);
+
+                i8 dest = '\0';
+                i32 length = string_process_escape_seqs((char *) &dest, char_lit->token->text, 1);
+                char_lit->value.i = (u32) dest;
+
+                if (length != 1) {
+                    onyx_report_error(char_lit->token->pos, Error_Critical, "Expected only a single character in character literal.");
+                }
+
+                retval = (AstTyped *) char_lit;
+                break;
+            }
+            else if (parse_possible_directive(parser, "type")) {
+                AstTypeAlias* alias = make_node(AstTypeAlias, Ast_Kind_Type_Alias);
+                alias->token = parser->curr - 2;
+                alias->to = parse_type(parser);
+                retval = (AstTyped *) alias;
+                break;
+            }
+            else if (parse_possible_directive(parser, "solidify")) {
+                AstDirectiveSolidify* solid = make_node(AstDirectiveSolidify, Ast_Kind_Directive_Solidify);
+                // :LinearTokenDependent
+                solid->token = parser->curr - 1;
+
+                solid->poly_proc = (AstFunction *) parse_factor(parser);
+
+                solid->known_polyvars = NULL;
+                bh_arr_new(global_heap_allocator, solid->known_polyvars, 2);
+
+                expect_token(parser, '{');
+                while (!consume_token_if_next(parser, '}')) {
+                    if (parser->hit_unexpected_token) break;
+
+                    AstNode* poly_var = make_node(AstNode, Ast_Kind_Symbol);
+                    poly_var->token = expect_token(parser, Token_Type_Symbol);
+
+                    expect_token(parser, '=');
+                    AstType* poly_type = (AstType *) parse_expression(parser, 0);
+
+                    bh_arr_push(solid->known_polyvars, ((AstPolySolution) {
+                        .kind     = PSK_Undefined,
+                        .poly_sym = poly_var,
+                        .ast_type = poly_type,
+                        .type     = NULL
+                    }));
+
+                    if (parser->curr->type != '}')
+                        expect_token(parser, ',');
+                }
+
+                retval = (AstTyped *) solid;
+                break;
+            }
+            else if (parse_possible_directive(parser, "defined")) {
+                AstDirectiveDefined* defined = make_node(AstDirectiveDefined, Ast_Kind_Directive_Defined);
+                // :LinearTokenDependent
+                defined->token = parser->curr - 1;
+                defined->type_node = (AstType *) &basic_type_bool;
+
+                expect_token(parser, '(');
+                defined->expr = parse_expression(parser, 0);
+                expect_token(parser, ')');
+
+                retval = (AstTyped *) defined;
+                break;
+            }
+            else if (parse_possible_directive(parser, "quote")) {
+                OnyxToken* code_token = parser->curr - 1;
+
+                AstCodeBlock* code_block = make_node(AstCodeBlock, Ast_Kind_Code_Block);
+                code_block->token = code_token;
+
+                assert(builtin_code_type != NULL);
+                code_block->type_node = builtin_code_type;
+
+                if (parser->curr->type == '{') {
+                    code_block->code = (AstNode *) parse_block(parser, 1, NULL);
+                    ((AstBlock *) code_block->code)->rules = Block_Rule_Code_Block;
+
+                } else {
+                    code_block->code = (AstNode *) parse_expression(parser, 0);
+                }
+
+                retval = (AstTyped *) code_block;
+                break;
+            }
+            else if (next_tokens_are(parser, 2, '#', '(')) {
+                OnyxToken* code_token = expect_token(parser, '#');
+                expect_token(parser, '(');
+
+                AstCodeBlock* code_block = make_node(AstCodeBlock, Ast_Kind_Code_Block);
+                code_block->token = code_token;
+
+                assert(builtin_code_type != NULL);
+                code_block->type_node = builtin_code_type;
+
+                code_block->code = (AstNode *) parse_expression(parser, 0);
+
+                expect_token(parser, ')');
+
+                retval = (AstTyped *) code_block;
+                break;
+            }
+            else if (parse_possible_directive(parser, "unquote")) {
+                AstDirectiveInsert* insert = make_node(AstDirectiveInsert, Ast_Kind_Directive_Insert);
+                insert->token = parser->curr - 1;
+                insert->code_expr = parse_expression(parser, 0);
+
+                retval = (AstTyped *) insert;
+                break;
+            }
+            else if (parse_possible_directive(parser, "cstr")) {
+                // Copy pasted from above.
+                AstStrLit* str_node = make_node(AstStrLit, Ast_Kind_StrLit);
+                str_node->token     = expect_token(parser, Token_Type_Literal_String);
+                str_node->data_id   = 0;
+                str_node->flags    |= Ast_Flag_Comptime;
+                str_node->is_cstr   = 1;
+
+                ENTITY_SUBMIT(str_node);
+
+                retval = (AstTyped *) str_node;
+                break;
+            }
+
+            onyx_report_error(parser->curr->pos, Error_Critical, "Invalid directive in expression.");
+            return NULL;
+        }
+
+        default:
+        no_match:
+            onyx_report_error(parser->curr->pos, Error_Critical, "Unexpected token '%s'.", token_name(parser->curr->type));
+            return NULL;
+    }
+
+    while (1) {
+        if (parser->hit_unexpected_token) return retval;
+
+        switch ((u16) parser->curr->type) {
+            case '[': {
+                OnyxToken *open_bracket = expect_token(parser, '[');
+                AstTyped *expr = parse_compound_expression(parser, 0);
+
+                AstSubscript *sub_node = make_node(AstSubscript, Ast_Kind_Subscript);
+                sub_node->token = open_bracket;
+                sub_node->addr = retval;
+                sub_node->expr = expr;
+                sub_node->__unused_operation = Binary_Op_Subscript;
+
+                retval = (AstTyped *) sub_node;
+                expect_token(parser, ']');
+                break;
+            }
+
+            case '.': {
+                if (parse_possible_struct_literal(parser, retval, &retval)) return retval;
+                if (parse_possible_array_literal(parser, retval, &retval))  return retval;
+
+                consume_token(parser);
+                AstFieldAccess* field = make_node(AstFieldAccess, Ast_Kind_Field_Access);
+                field->token = expect_token(parser, Token_Type_Symbol);
+                field->expr  = retval;
+
+                retval = (AstTyped *) field;
+                break;
+            }
+
+            case '(': {
+                if (!parser->parse_calls) goto factor_parsed;
+
+                AstCall* call_node = make_node(AstCall, Ast_Kind_Call);
+                call_node->token = expect_token(parser, '(');
+                call_node->callee = retval;
+
+                arguments_initialize(&call_node->args);
+
+                parse_arguments(parser, ')', &call_node->args);
+
+                // Wrap expressions in AstArgument
+                bh_arr_each(AstTyped *, arg, call_node->args.values) {
+                    if ((*arg) == NULL) continue;
+                    *arg = (AstTyped *) make_argument(parser->allocator, *arg);
+                }
+
+                bh_arr_each(AstNamedValue *, named_value, call_node->args.named_values) {
+                    if ((*named_value)->value == NULL) continue;
+                    (*named_value)->value = (AstTyped *) make_argument(parser->allocator, (AstTyped *) (*named_value)->value);
+                }
+
+                retval = (AstTyped *) call_node;
+                break;
+            }
+
+            case Token_Type_Keyword_If: {
+                AstIfExpression* if_expression = make_node(AstIfExpression, Ast_Kind_If_Expression);
+                if_expression->token = expect_token(parser, Token_Type_Keyword_If);
+
+                if_expression->true_expr  = retval;
+                if_expression->cond       = parse_expression(parser, 0);
+                expect_token(parser, Token_Type_Keyword_Else);
+                if_expression->false_expr = parse_expression(parser, 0);
+
+                retval = (AstTyped *) if_expression;
+                break;
+            }
+
+            default: goto factor_parsed;
+        }
+    }
+
+factor_parsed:
+
+    return retval;
+}
+
+static inline i32 get_precedence(BinaryOp kind) {
+    switch (kind) {
+        case Binary_Op_Assign:          return 1;
+        case Binary_Op_Assign_Add:      return 1;
+        case Binary_Op_Assign_Minus:    return 1;
+        case Binary_Op_Assign_Multiply: return 1;
+        case Binary_Op_Assign_Divide:   return 1;
+        case Binary_Op_Assign_Modulus:  return 1;
+        case Binary_Op_Assign_And:      return 1;
+        case Binary_Op_Assign_Or:       return 1;
+        case Binary_Op_Assign_Xor:      return 1;
+        case Binary_Op_Assign_Shl:      return 1;
+        case Binary_Op_Assign_Shr:      return 1;
+        case Binary_Op_Assign_Sar:      return 1;
+
+        case Binary_Op_Pipe:            return 2;
+        case Binary_Op_Range:           return 2;
+
+        case Binary_Op_Bool_And:        return 3;
+        case Binary_Op_Bool_Or:         return 3;
+
+        case Binary_Op_Equal:           return 4;
+        case Binary_Op_Not_Equal:       return 4;
+
+        case Binary_Op_Less_Equal:      return 5;
+        case Binary_Op_Less:            return 5;
+        case Binary_Op_Greater_Equal:   return 5;
+        case Binary_Op_Greater:         return 5;
+
+        case Binary_Op_And:             return 6;
+        case Binary_Op_Or:              return 6;
+        case Binary_Op_Xor:             return 6;
+        case Binary_Op_Shl:             return 6;
+        case Binary_Op_Shr:             return 6;
+        case Binary_Op_Sar:             return 6;
+
+        case Binary_Op_Add:             return 7;
+        case Binary_Op_Minus:           return 7;
+
+        case Binary_Op_Multiply:        return 8;
+        case Binary_Op_Divide:          return 8;
+
+        case Binary_Op_Modulus:         return 9;
+        
+        case Binary_Op_Method_Call:     return 10;
+
+        default:                        return -1;
+    }
+}
+
+static BinaryOp binary_op_from_token_type(TokenType t) {
+    switch ((u16) t) {
+        case Token_Type_Equal_Equal:       return Binary_Op_Equal;
+        case Token_Type_Not_Equal:         return Binary_Op_Not_Equal;
+        case Token_Type_Less_Equal:        return Binary_Op_Less_Equal;
+        case Token_Type_Greater_Equal:     return Binary_Op_Greater_Equal;
+        case '<':                          return Binary_Op_Less;
+        case '>':                          return Binary_Op_Greater;
+
+        case '+':                          return Binary_Op_Add;
+        case '-':                          return Binary_Op_Minus;
+        case '*':                          return Binary_Op_Multiply;
+        case '/':                          return Binary_Op_Divide;
+        case '%':                          return Binary_Op_Modulus;
+
+        case '&':                          return Binary_Op_And;
+        case '|':                          return Binary_Op_Or;
+        case '^':                          return Binary_Op_Xor;
+        case Token_Type_Shift_Left:        return Binary_Op_Shl;
+        case Token_Type_Shift_Right:       return Binary_Op_Shr;
+        case Token_Type_Shift_Arith_Right: return Binary_Op_Sar;
+
+        case Token_Type_And_And:           return Binary_Op_Bool_And;
+        case Token_Type_Or_Or:             return Binary_Op_Bool_Or;
+
+        case '=':                          return Binary_Op_Assign;
+        case Token_Type_Plus_Equal:        return Binary_Op_Assign_Add;
+        case Token_Type_Minus_Equal:       return Binary_Op_Assign_Minus;
+        case Token_Type_Star_Equal:        return Binary_Op_Assign_Multiply;
+        case Token_Type_Fslash_Equal:      return Binary_Op_Assign_Divide;
+        case Token_Type_Percent_Equal:     return Binary_Op_Assign_Modulus;
+        case Token_Type_And_Equal:         return Binary_Op_Assign_And;
+        case Token_Type_Or_Equal:          return Binary_Op_Assign_Or;
+        case Token_Type_Xor_Equal:         return Binary_Op_Assign_Xor;
+        case Token_Type_Shl_Equal:         return Binary_Op_Assign_Shl;
+        case Token_Type_Shr_Equal:         return Binary_Op_Assign_Shr;
+        case Token_Type_Sar_Equal:         return Binary_Op_Assign_Sar;
+
+        case Token_Type_Pipe:              return Binary_Op_Pipe;
+        case Token_Type_Dot_Dot:           return Binary_Op_Range;
+        case '[':                          return Binary_Op_Subscript;
+        case Token_Type_Right_Arrow:       return Binary_Op_Method_Call;
+        default: return Binary_Op_Count;
+    }
+}
+
+static AstTyped* parse_compound_assignment(OnyxParser* parser, AstTyped* lhs) {
+    if (parser->curr->type != '=') return lhs;
+
+    AstBinaryOp* assignment = make_node(AstBinaryOp, Ast_Kind_Binary_Op);
+    assignment->token = expect_token(parser, '=');
+    assignment->operation = Binary_Op_Assign;
+    assignment->left = lhs;
+    assignment->right = parse_compound_expression(parser, 0);
+
+    return (AstTyped *) assignment;
+}
+
+static AstTyped* parse_compound_expression(OnyxParser* parser, b32 assignment_allowed) {
+    AstTyped* first = parse_expression(parser, assignment_allowed);
+
+    if (parser->curr->type == ',') {
+        AstCompound* compound = make_node(AstCompound, Ast_Kind_Compound);
+        compound->token = parser->curr;
+
+        bh_arr_new(global_heap_allocator, compound->exprs, 2);
+        bh_arr_push(compound->exprs, first);
+
+        while (consume_token_if_next(parser, ',')) {
+            if (parser->hit_unexpected_token) return (AstTyped *) compound;
+
+            AstTyped* expr = parse_expression(parser, 0);
+            bh_arr_push(compound->exprs, expr);
+
+            if (assignment_allowed && parser->curr->type == '=') {
+                return parse_compound_assignment(parser, (AstTyped *) compound);
+            }
+        }
+
+        return (AstTyped *) compound;
+
+    } else {
+        return first;
+    }
+}
+
+static AstTyped* parse_expression(OnyxParser* parser, b32 assignment_allowed) {
+    bh_arr(AstBinaryOp*) tree_stack = NULL;
+    bh_arr_new(global_heap_allocator, tree_stack, 4);
+    bh_arr_set_length(tree_stack, 0);
+
+    AstTyped* left = parse_factor(parser);
+    AstTyped* right;
+    AstTyped* root = left;
+
+    BinaryOp bin_op_kind;
+    OnyxToken* bin_op_tok;
+
+    while (1) {
+        if (parser->hit_unexpected_token) return root;
+
+        bin_op_kind = binary_op_from_token_type(parser->curr->type);
+        if (bin_op_kind == Binary_Op_Count) goto expression_done;
+        if (binop_is_assignment(bin_op_kind) && !assignment_allowed) goto expression_done;
+        if (bin_op_kind == Binary_Op_Subscript) goto expression_done;
+
+        bin_op_tok = parser->curr;
+        consume_token(parser);
+
+        AstBinaryOp* bin_op;
+        if      (bin_op_kind == Binary_Op_Pipe)        bin_op = make_node(AstBinaryOp, Ast_Kind_Pipe);
+        else if (bin_op_kind == Binary_Op_Method_Call) bin_op = make_node(AstBinaryOp, Ast_Kind_Method_Call);
+        else if (bin_op_kind == Binary_Op_Range)       bin_op = (AstBinaryOp *) make_node(AstRangeLiteral, Ast_Kind_Range_Literal);
+        else                                           bin_op = make_node(AstBinaryOp, Ast_Kind_Binary_Op);
+
+        bin_op->token = bin_op_tok;
+        bin_op->operation = bin_op_kind;
+
+        while ( !bh_arr_is_empty(tree_stack) &&
+                get_precedence(bh_arr_last(tree_stack)->operation) >= get_precedence(bin_op_kind))
+            bh_arr_pop(tree_stack);
+
+        if (bh_arr_is_empty(tree_stack)) {
+            // NOTE: new is now the root node
+            bin_op->left = root;
+            root = (AstTyped *) bin_op;
+
+        } else {
+            bin_op->left = bh_arr_last(tree_stack)->right;
+            bh_arr_last(tree_stack)->right = (AstTyped *) bin_op;
+        }
+
+        bh_arr_push(tree_stack, bin_op);
+
+        right = parse_factor(parser);
+        bin_op->right = right;
+    }
+
+expression_done:
+    bh_arr_free(tree_stack);
+    return root;
+}
+
+static AstIfWhile* parse_if_stmt(OnyxParser* parser) {
+    AstIfWhile* if_node = make_node(AstIfWhile, Ast_Kind_If);
+    if_node->token = expect_token(parser, Token_Type_Keyword_If);
+
+    AstIfWhile* root_if = if_node;
+    AstTyped* cond;
+    AstNode* initialization_or_cond=NULL;
+    b32 had_initialization = 0;
+    if (parse_possible_symbol_declaration(parser, &initialization_or_cond)) {
+        had_initialization = 1;
+
+    } else {
+        // NOTE: Assignment is allowed here because instead of not parsing correctly,
+        // an error is reported in the typechecking, saying that assignment isn't allowed
+        // here, which is better than an unexpected token error.
+        initialization_or_cond = (AstNode *) parse_compound_expression(parser, 1);
+    }
+
+    if (had_initialization || parser->curr->type == ';') {
+        expect_token(parser, ';');
+        cond = parse_expression(parser, 1);
+    } else {
+        cond = (AstTyped *) initialization_or_cond;
+        initialization_or_cond = NULL;
+    }
+
+    AstBlock* true_stmt = parse_block(parser, 1, NULL);
+
+    if_node->initialization = initialization_or_cond;
+    if_node->cond = cond;
+    if (true_stmt != NULL)
+        if_node->true_stmt = true_stmt;
+
+    while (consume_token_if_next(parser, Token_Type_Keyword_Elseif)) {
+        if (parser->hit_unexpected_token) return root_if;
+
+        AstIfWhile* elseif_node = make_node(AstIfWhile, Ast_Kind_If);
+        elseif_node->token = parser->curr - 1;
+
+        cond = parse_expression(parser, 1);
+        true_stmt = parse_block(parser, 1, NULL);
+
+        elseif_node->cond = cond;
+        if (true_stmt != NULL)
+            elseif_node->true_stmt = true_stmt;
+
+        if_node->false_stmt = (AstBlock *) elseif_node;
+        if_node = elseif_node;
+    }
+
+    if (consume_token_if_next(parser, Token_Type_Keyword_Else)) {
+        AstBlock* false_stmt = parse_block(parser, 1, NULL);
+        if (false_stmt != NULL)
+            if_node->false_stmt = false_stmt;
+    }
+
+    return root_if;
+}
+
+static AstIfWhile* parse_while_stmt(OnyxParser* parser) {
+    OnyxToken* while_token = expect_token(parser, Token_Type_Keyword_While);
+    AstIfWhile* while_node = make_node(AstIfWhile, Ast_Kind_While);
+    while_node->token = while_token;
+
+    if (parse_possible_directive(parser, "bottom_test")) {
+        while_node->bottom_test = 1;
+    }
+
+    AstTyped* cond;
+    AstNode* initialization_or_cond=NULL;
+    b32 had_initialization = 0;
+    if (parse_possible_symbol_declaration(parser, &initialization_or_cond)) {
+        had_initialization = 1;
+
+    } else {
+        // NOTE: Assignment is allowed here because instead of not parsing correctly,
+        // an error is reported in the typechecking, saying that assignment isn't allowed
+        // here, which is better than an unexpected token error.
+        initialization_or_cond = (AstNode *) parse_compound_expression(parser, 1);
+    }
+
+    if (had_initialization || parser->curr->type == ';') {
+        expect_token(parser, ';');
+        cond = parse_expression(parser, 1);
+    } else {
+        cond = (AstTyped *) initialization_or_cond;
+        initialization_or_cond = NULL;
+    }
+
+    while_node->initialization = initialization_or_cond;
+    while_node->cond = cond;
+    while_node->true_stmt = parse_block(parser, 1, NULL);
+
+    if (consume_token_if_next(parser, Token_Type_Keyword_Else)) {
+        while_node->false_stmt = parse_block(parser, 1, NULL);
+    }
+
+    return while_node;
+}
+
+static AstFor* parse_for_stmt(OnyxParser* parser) {
+    AstFor* for_node = make_node(AstFor, Ast_Kind_For);
+    for_node->token = expect_token(parser, Token_Type_Keyword_For);
+
+    if (parse_possible_directive(parser, "no_close")) {
+        for_node->no_close = 1;
+    }
+
+    if (consume_token_if_next(parser, '^')) {
+        for_node->by_pointer = 1;
+    }
+
+    if (next_tokens_are(parser, 2, Token_Type_Symbol, ':')) {
+        OnyxToken* local_sym = expect_token(parser, Token_Type_Symbol);
+        AstLocal* var_node = make_local(parser->allocator, local_sym, NULL);
+
+        for_node->var = var_node;
+
+        expect_token(parser, ':');
+    } else {
+        // HACK
+        static char it_name[] = "it ";
+        static OnyxToken it_token = { Token_Type_Symbol, 2, it_name, { 0 } };
+
+        AstLocal* var_node = make_local(parser->allocator, &it_token, NULL);
+        for_node->var = var_node;
+    }
+
+    for_node->iter = parse_expression(parser, 1);
+    for_node->stmt = parse_block(parser, 1, NULL);
+
+    return for_node;
+}
+
+static AstSwitchCase* parse_case_stmt(OnyxParser* parser) {
+    AstSwitchCase *sc_node = make_node(AstSwitchCase, Ast_Kind_Switch_Case);
+    sc_node->token = expect_token(parser, Token_Type_Keyword_Case);
+
+    if (parse_possible_directive(parser, "default")) {
+        sc_node->is_default = 1;
+
+    } else {
+        bh_arr_new(global_heap_allocator, sc_node->values, 1);
+
+        AstTyped* value = parse_expression(parser, 1);
+        bh_arr_push(sc_node->values, value);
+        while (consume_token_if_next(parser, ',')) {
+            if (parser->hit_unexpected_token) return sc_node;
+
+            value = parse_expression(parser, 1);
+            bh_arr_push(sc_node->values, value);
+        }
+    }
+
+    sc_node->block = parse_block(parser, 1, NULL);
+
+    return sc_node;
+}
+
+static AstSwitch* parse_switch_stmt(OnyxParser* parser) {
+    AstSwitch* switch_node = make_node(AstSwitch, Ast_Kind_Switch);
+    switch_node->token = expect_token(parser, Token_Type_Keyword_Switch);
+
+    AstTyped* expr;
+    AstNode* initialization_or_expr=NULL;
+    b32 had_initialization = 0;
+    if (parse_possible_symbol_declaration(parser, &initialization_or_expr)) {
+        had_initialization = 1;
+
+    } else {
+        // NOTE: Assignment is allowed here because instead of not parsing correctly,
+        // an error is reported in the typechecking, saying that assignment isn't allowed
+        // here, which is better than an unexpected token error.
+        initialization_or_expr = (AstNode *) parse_compound_expression(parser, 1);
+    }
+
+    if (had_initialization || parser->curr->type == ';') {
+        expect_token(parser, ';');
+        expr = parse_expression(parser, 1);
+
+    } else {
+        expr = (AstTyped *) initialization_or_expr;
+        initialization_or_expr = NULL;
+    }
+
+    switch_node->initialization = initialization_or_expr;
+    switch_node->expr = expr;
+
+    switch_node->case_block = parse_block(parser, 1, NULL);
+    return switch_node;
+}
+
+static i32 parse_possible_compound_symbol_declaration(OnyxParser* parser, AstNode** ret) {
+    u32 token_offset = 0;
+    while (peek_token(token_offset)->type == Token_Type_Symbol) {
+        token_offset += 1;
+
+        if (peek_token(token_offset)->type == '\'') token_offset += 1;
+
+        if (peek_token(token_offset)->type != ',') break;
+        token_offset += 1;
+    }
+
+    if (peek_token(token_offset)->type != ':') return 0;
+
+    // At this point, we are sure it is a compound declaration.
+    AstCompound* local_compound = make_node(AstCompound, Ast_Kind_Compound);
+    bh_arr_new(global_heap_allocator, local_compound->exprs, token_offset / 2);
+
+    AstLocal* first_local = NULL;
+    AstLocal* prev_local  = NULL;
+
+    while (parser->curr->type == Token_Type_Symbol) {
+        if (parser->hit_unexpected_token) return 1;
+
+        OnyxToken* local_sym = expect_token(parser, Token_Type_Symbol);
+        AstNode* sym_node = make_symbol(parser->allocator, local_sym);
+        bh_arr_push(local_compound->exprs, (AstTyped *) sym_node);
+
+        if (!consume_token_if_next(parser, '\'')) {
+            AstLocal* new_local = make_local(parser->allocator, local_sym, NULL);
+            if (prev_local == NULL) {
+                first_local = new_local;
+            } else {
+                prev_local->next = (AstNode *) new_local;
+            }
+            prev_local = new_local;
+        }
+
+        consume_token_if_next(parser, ',');
+    }
+
+    expect_token(parser, ':');
+
+    if (parser->curr->type == '=') {
+        AstBinaryOp* assignment = make_binary_op(parser->allocator, Binary_Op_Assign, (AstTyped *) local_compound, NULL);
+        assignment->token = expect_token(parser, '=');
+        assignment->right = parse_compound_expression(parser, 0);
+
+        prev_local->next = (AstNode *) assignment;
+
+    } else {
+        AstType* type_for_all = parse_type(parser);
+        forll (AstLocal, local, first_local, next) {
+            local->type_node = type_for_all;
+        }
+    }
+
+    *ret = (AstNode *) first_local;
+    return 1;
+}
+
+// Returns:
+//     0 - if this was not a symbol declaration.
+//     1 - if this was a local declaration.
+//     2 - if this was binding declaration.
+// ret is set to the statement to insert
+static i32 parse_possible_symbol_declaration(OnyxParser* parser, AstNode** ret) {
+    // Has to start with a symbol to be a declaration
+    if (parser->curr->type != Token_Type_Symbol) return 0;
+
+    // If the token after the symbol is a comma, assume this is a compound declaration.
+    if (peek_token(1)->type == ',' ||
+        (peek_token(1)->type == '\'' && peek_token(2)->type == ',')) {
+        return parse_possible_compound_symbol_declaration(parser, ret);
+    }
+
+    if (peek_token(1)->type != ':') return 0;
+
+    OnyxToken* symbol = expect_token(parser, Token_Type_Symbol);
+    expect_token(parser, ':');
+
+    if (parser->curr->type == ':') {
+        bh_arr_push(parser->current_symbol_stack, symbol);
+        AstBinding* binding = parse_top_level_binding(parser, symbol);
+        bh_arr_pop(parser->current_symbol_stack);
+        if (parser->hit_unexpected_token) return 2;
+
+        ENTITY_SUBMIT(binding);
+        return 2;
+    }
+
+    AstType* type_node = NULL;
+    if (parser->curr->type != '=') {
+        type_node = parse_type(parser);
+    }
+
+    AstLocal* local = make_local(parser->allocator, symbol, type_node);
+    *ret = (AstNode *) local;
+
+    if (parser->curr->type == '=') {
+        AstBinaryOp* assignment = make_node(AstBinaryOp, Ast_Kind_Binary_Op);
+        assignment->operation = Binary_Op_Assign;
+        assignment->token = expect_token(parser, '=');
+        local->next = (AstNode *) assignment;
+
+        AstTyped* expr = parse_expression(parser, 1);
+        if (expr == NULL) return 1;
+        assignment->right = expr;
+
+        // INVESTIGATE: I don't know why, but appearantly, this has to be a
+        // symbol node, not a direct link to the local. There is an error about
+        // being unable to resolve the type of the local if it is immediately set.
+        AstNode* left_symbol = make_node(AstNode, Ast_Kind_Symbol);
+        left_symbol->token = symbol;
+        assignment->left = (AstTyped *) left_symbol;
+    }
+
+    return 1;
+}
+
+static AstReturn* parse_return_stmt(OnyxParser* parser) {
+    AstReturn* return_node = make_node(AstReturn, Ast_Kind_Return);
+    return_node->token = expect_token(parser, Token_Type_Keyword_Return);
+
+    AstTyped* expr = NULL;
+
+    if (parser->curr->type != ';') {
+        expr = parse_compound_expression(parser, 0);
+
+        if (expr == NULL || expr == (AstTyped *) &error_node) {
+            return (AstReturn *) &error_node;
+        } else {
+            return_node->expr = expr;
+        }
+    }
+
+    return return_node;
+}
+
+static AstNode* parse_use_stmt(OnyxParser* parser) {
+    OnyxToken* use_token = expect_token(parser, Token_Type_Keyword_Use);
+    AstUse* use_node = make_node(AstUse, Ast_Kind_Use);
+    use_node->token = use_token;
+    use_node->expr = parse_expression(parser, 1);
+
+    if (consume_token_if_next(parser, '{')) {
+        bh_arr_new(global_heap_allocator, use_node->only, 4);
+
+        while (!consume_token_if_next(parser, '}')) {
+            if (parser->hit_unexpected_token) return NULL;
+
+            QualifiedUse qu;
+            qu.as_name = expect_token(parser, Token_Type_Symbol);
+            qu.symbol_name = qu.as_name;
+
+            if (consume_token_if_next(parser, ':')) {
+                expect_token(parser, ':');
+                qu.symbol_name = expect_token(parser, Token_Type_Symbol);
+            }
+
+            bh_arr_push(use_node->only, qu);
+
+            if (parser->curr->type != '}')
+                expect_token(parser, ',');
+        }
+    }
+
+    if (use_node->expr->kind == Ast_Kind_Package) {
+        ENTITY_SUBMIT(use_node);
+        return NULL;
+
+    } else {
+        return (AstNode *) use_node;
+    }
+}
+
+static AstNode* parse_jump_stmt(OnyxParser* parser, TokenType token_type, JumpType jump_type) {
+    AstJump* jnode = make_node(AstJump, Ast_Kind_Jump);
+    jnode->token = expect_token(parser, token_type);
+    jnode->jump  = jump_type;
+
+    u64 count = 1;
+    while (consume_token_if_next(parser, token_type)) count++;
+    jnode->count = count;
+
+    return (AstNode *) jnode;
+}
+
+static AstNode* parse_statement(OnyxParser* parser) {
+    b32 needs_semicolon = 1;
+    AstNode* retval = NULL;
+
+    switch ((u16) parser->curr->type) {
+        case Token_Type_Keyword_Return:
+            retval = (AstNode *) parse_return_stmt(parser);
+            break;
+
+        case '{':
+        case Token_Type_Empty_Block:
+        case Token_Type_Keyword_Do:
+            needs_semicolon = 0;
+            retval = (AstNode *) parse_block(parser, 1, NULL);
+            break;
+
+        case Token_Type_Symbol: {
+            i32 symbol_res = parse_possible_symbol_declaration(parser, &retval);
+            if (symbol_res == 2) needs_semicolon = 0;
+            if (symbol_res != 0) break;
+
+            // fallthrough
+        }
+
+        case '(': case '+': case '-': case '!': case '*': case '^':
+        case Token_Type_Literal_Integer:
+        case Token_Type_Literal_Float:
+        case Token_Type_Literal_String:
+            retval = (AstNode *) parse_compound_expression(parser, 1);
+            if (retval->kind == Ast_Kind_Call || retval->kind == Ast_Kind_Method_Call) {
+                if (parser->curr->type == '{') {
+                    AstCodeBlock* code_block = make_node(AstCodeBlock, Ast_Kind_Code_Block);
+                    code_block->token = parser->curr;
+                    code_block->type_node = builtin_code_type;
+
+                    code_block->code = (AstNode *) parse_block(parser, 1, NULL);
+                    ((AstBlock *) code_block->code)->rules = Block_Rule_Code_Block;
+
+                    AstCall *dest = (AstCall *) retval;
+                    if (dest->kind == Ast_Kind_Method_Call) {
+                        dest = (AstCall *) ((AstBinaryOp *) dest)->right;
+                        if (dest->kind != Ast_Kind_Call) {
+                            onyx_report_error(retval->token->pos, Error_Critical, "Expected function call on right side of '->'.");
+                            needs_semicolon = 0;
+                            break;
+                        }
+                    }
+
+                    bh_arr_push(dest->args.values, (AstTyped *) make_argument(context.ast_alloc, (AstTyped *) code_block));
+                    needs_semicolon = 0;
+                }
+            }
+            break;
+
+        case Token_Type_Keyword_If:
+            needs_semicolon = 0;
+            retval = (AstNode *) parse_if_stmt(parser);
+            break;
+
+        case Token_Type_Keyword_While:
+            needs_semicolon = 0;
+            retval = (AstNode *) parse_while_stmt(parser);
+            break;
+
+        case Token_Type_Keyword_For:
+            needs_semicolon = 0;
+            retval = (AstNode *) parse_for_stmt(parser);
+            break;
+
+        case Token_Type_Keyword_Switch:
+            needs_semicolon = 0;
+            retval = (AstNode *) parse_switch_stmt(parser);
+            break;
+
+        case Token_Type_Keyword_Case:
+            needs_semicolon = 0;
+            retval = (AstNode *) parse_case_stmt(parser);
+            break;
+
+        case Token_Type_Keyword_Break:
+            retval = parse_jump_stmt(parser, Token_Type_Keyword_Break, Jump_Type_Break);
+            break;
+
+        case Token_Type_Keyword_Continue:
+            retval = parse_jump_stmt(parser, Token_Type_Keyword_Continue, Jump_Type_Continue);
+            break;
+
+        case Token_Type_Keyword_Fallthrough:
+            retval = parse_jump_stmt(parser, Token_Type_Keyword_Fallthrough, Jump_Type_Fallthrough);
+            break;
+
+        case Token_Type_Keyword_Defer: {
+            needs_semicolon = 0;
+
+            AstDefer* defer = make_node(AstDefer, Ast_Kind_Defer);
+            defer->token = expect_token(parser, Token_Type_Keyword_Defer);
+            defer->stmt  = parse_statement(parser);
+
+            retval = (AstNode *) defer;
+            break;
+        }
+
+        case Token_Type_Keyword_Use: {
+            needs_semicolon = 0;
+
+            retval = (AstNode *) parse_use_stmt(parser);
+            break;
+        }
+
+        case '#': {
+            if (parse_possible_directive(parser, "context_scope")) {
+                // :LinearTokenDependent
+                OnyxToken* directive_token = parser->curr - 2;
+
+                OnyxToken* sym_token = bh_alloc_item(parser->allocator, OnyxToken);
+                sym_token->type = Token_Type_Symbol;
+                sym_token->length = 15;
+                sym_token->text = bh_strdup(parser->allocator, "__saved_context ");
+                sym_token->pos = ((OnyxFilePos) {0});
+
+                AstNode *sym_node = make_symbol(parser->allocator, sym_token);
+
+                AstLocal* context_tmp = make_local(parser->allocator, sym_token, NULL);
+                retval = (AstNode *) context_tmp;
+
+                AstBinaryOp* assignment = make_node(AstBinaryOp, Ast_Kind_Binary_Op);
+                assignment->token = directive_token;
+                assignment->operation = Binary_Op_Assign;
+                assignment->left = (AstTyped *) sym_node;
+                assignment->right = builtin_context_variable;
+                context_tmp->next = (AstNode *) assignment;
+
+                AstBinaryOp* assignment2 = make_node(AstBinaryOp, Ast_Kind_Binary_Op);
+                assignment2->token = directive_token + 1;
+                assignment2->operation = Binary_Op_Assign;
+                assignment2->left = builtin_context_variable;
+                assignment2->right = (AstTyped *) sym_node;
+
+                AstDefer* defer_node = make_node(AstDefer, Ast_Kind_Defer);
+                defer_node->token = directive_token;
+                defer_node->stmt = (AstNode *) assignment2;
+                assignment->next = (AstNode *) defer_node;
+
+                AstBlock* context_block = parse_block(parser, 1, NULL);
+                defer_node->next = context_block->body;
+                context_block->body = (AstNode *) context_tmp;
+
+                needs_semicolon = 0;
+                break;
+            }
+
+            if (next_tokens_are(parser, 2, '#', Token_Type_Keyword_If)) {
+                AstIf* static_if = parse_static_if_stmt(parser, 1);
+
+                assert(parser->current_function_stack && bh_arr_length(parser->current_function_stack) > 0);
+                bh_arr_push(bh_arr_last(parser->current_function_stack)->nodes_that_need_entities_after_clone, (AstNode *) static_if);
+
+                needs_semicolon = 0;
+                retval = (AstNode *) static_if;
+                break;
+            }
+
+            if (parse_possible_directive(parser, "persist")) {
+                b32 thread_local = parse_possible_directive(parser, "thread_local");
+
+                OnyxToken* symbol = expect_token(parser, Token_Type_Symbol);
+                AstMemRes* memres = parse_memory_reservation(parser, symbol, thread_local);
+
+                AstBinding* binding = make_node(AstBinding, Ast_Kind_Binding);
+                binding->token = memres->token;
+                binding->node = (AstNode *) memres;
+                ENTITY_SUBMIT(binding);
+                break;
+            }
+
+            if (parse_possible_directive(parser, "remove")) {
+                // :LinearTokenDependent
+                AstDirectiveRemove *remove = make_node(AstDirectiveRemove, Ast_Kind_Directive_Remove);
+                remove->token = parser->curr - 2;
+                retval = (AstNode *) remove;
+                break;
+            }
+
+            if (next_tokens_are(parser, 2, '#', Token_Type_Symbol)) {
+                retval = (AstNode *) parse_factor(parser);
+                break;
+            }
+        }
+
+        default:
+            break;
+    }
+
+    if (needs_semicolon) expect_token(parser, ';');
+
+    return retval;
+}
+
+static AstBlock* parse_block(OnyxParser* parser, b32 make_a_new_scope, char* block_name) {
+    AstBlock* block = make_node(AstBlock, Ast_Kind_Block);
+    block->rules = Block_Rule_Normal;
+
+    // NOTE: --- is for an empty block
+    if (parser->curr->type == Token_Type_Empty_Block) {
+        block->token = expect_token(parser, Token_Type_Empty_Block);
+        return block;
+    }
+
+    if (make_a_new_scope) {
+        block->binding_scope = scope_create(parser->allocator, parser->current_scope, parser->curr->pos);
+        block->binding_scope->name = block_name;
+        parser->current_scope = block->binding_scope;
+    }
+
+    if (parser->curr->type == Token_Type_Keyword_Do) {
+        block->token = expect_token(parser, Token_Type_Keyword_Do);
+        block->body = parse_statement(parser);
+        if (make_a_new_scope) parser->current_scope = parser->current_scope->parent;
+        return block;
+    }
+
+    block->token = expect_token(parser, '{');
+
+    AstNode** next = &block->body;
+    AstNode* stmt = NULL;
+    while (!consume_token_if_next(parser, '}')) {
+        if (parser->hit_unexpected_token) {
+            if (make_a_new_scope) parser->current_scope = parser->current_scope->parent;
+            return block;
+        }
+
+        stmt = parse_statement(parser);
+
+        if (stmt != NULL && stmt->kind != Ast_Kind_Error) {
+            *next = stmt;
+
+            while (stmt->next != NULL) stmt = stmt->next;
+            next = &stmt->next;
+        }
+    }
+
+    if (make_a_new_scope) parser->current_scope = parser->current_scope->parent;
+    return block;
+}
+
+static void parse_polymorphic_variable(OnyxParser* parser, AstType*** next_insertion) {
+    bh_arr(AstPolyParam) pv = NULL;
+
+    if (parser->polymorph_context.poly_params == NULL)
+        onyx_report_error(parser->curr->pos, Error_Critical, "Polymorphic variable not valid here.");
+    else
+        pv = *parser->polymorph_context.poly_params;
+
+    consume_token(parser);
+
+    AstNode* symbol_node = make_node(AstNode, Ast_Kind_Symbol);
+    symbol_node->token = expect_token(parser, Token_Type_Symbol);
+
+    **next_insertion = (AstType *) symbol_node;
+    *next_insertion = NULL;
+
+    if (pv != NULL) {
+        bh_arr_push(pv, ((AstPolyParam) {
+            .kind     = PPK_Poly_Type,
+            .poly_sym = symbol_node,
+
+            // These will be filled out by function_params()
+            .type_expr = NULL,
+            .idx = -1,
+        }));
+
+        *parser->polymorph_context.poly_params = pv;
+    }
+}
+
+static AstType* parse_compound_type(OnyxParser* parser) {
+    // CLEANUP this is little weird having this here because it means that this parses:
+    //
+    //      foo :: (x: (something_here: i32)) -> void ---
+    //
+    if (next_tokens_are(parser, 2, Token_Type_Symbol, ':')) {
+        consume_tokens(parser, 2);
+    }
+
+    AstType* first = parse_type(parser);
+
+    if (parser->curr->type == ',') {
+        AstCompoundType* ctype = make_node(AstCompoundType, Ast_Kind_Type_Compound);
+        ctype->token = parser->curr;
+
+        bh_arr_new(global_heap_allocator, ctype->types, 2);
+        bh_arr_push(ctype->types, first);
+
+        while (consume_token_if_next(parser, ',')) {
+            if (parser->hit_unexpected_token) return (AstType *) ctype;
+
+            if (next_tokens_are(parser, 2, Token_Type_Symbol, ':')) {
+                consume_tokens(parser, 2);
+            }
+
+            bh_arr_push(ctype->types, parse_type(parser));
+        }
+
+        return (AstType *) ctype;
+
+    } else {
+        return first;
+    }
+}
+
+static AstType* parse_function_type(OnyxParser* parser, OnyxToken* proc_token) {
+    bh_arr(AstType *) params = NULL;
+    bh_arr_new(global_scratch_allocator, params, 4);
+    bh_arr_set_length(params, 0);
+
+    expect_token(parser, '(');
+    while (!consume_token_if_next(parser, ')')) {
+        if (parser->hit_unexpected_token) return NULL;
+
+        // NOTE: Allows for names to be put in the function types, just for readability.
+        if (next_tokens_are(parser, 2, Token_Type_Symbol, ':')) consume_tokens(parser, 2);
+
+        AstType* param_type = parse_type(parser);
+        bh_arr_push(params, param_type);
+
+        if (parser->curr->type != ')')
+            expect_token(parser, ',');
+    }
+
+    AstType* return_type = (AstType *) &basic_type_void;
+    if (consume_token_if_next(parser, Token_Type_Right_Arrow))
+        return_type = parse_type(parser);
+
+    i64 param_count = bh_arr_length(params);
+    AstFunctionType* new = onyx_ast_node_new(parser->allocator,
+            sizeof(AstFunctionType) + sizeof(AstType*) * param_count,
+            Ast_Kind_Function_Type);
+    new->token = proc_token;
+    new->param_count = param_count;
+    new->return_type = return_type;
+
+    if (param_count > 0)
+        fori (i, 0, param_count) new->params[i] = params[i];
+
+    return (AstType *) new;
+}
+
+static AstType* parse_type(OnyxParser* parser) {
+    AstType* root = NULL;
+    AstType** next_insertion = &root;
+
+    while (1) {
+        if (parser->hit_unexpected_token) return root;
+
+        switch ((u16) parser->curr->type) {
+            case '^': {
+                AstPointerType* new = make_node(AstPointerType, Ast_Kind_Pointer_Type);
+                new->flags |= Basic_Flag_Pointer;
+                new->token = expect_token(parser, '^');
+
+                *next_insertion = (AstType *) new;
+                next_insertion = &new->elem;
+                break;
+            }
+
+            case '[': {
+                AstType *new;
+                OnyxToken *open_bracket = expect_token(parser, '[');
+
+                if (parser->curr->type == ']') {
+                    new = make_node(AstSliceType, Ast_Kind_Slice_Type);
+                    new->token = open_bracket;
+
+                } else if (parser->curr->type == Token_Type_Dot_Dot) {
+                    new = make_node(AstDynArrType, Ast_Kind_DynArr_Type);
+                    new->token = open_bracket;
+                    consume_token(parser);
+
+                } else {
+                    new = make_node(AstArrayType, Ast_Kind_Array_Type);
+                    new->token = open_bracket;
+
+                    if (parser->curr->type == '$') {
+                        AstType** insertion = (AstType **) &((AstArrayType *) new)->count_expr;
+                        parse_polymorphic_variable(parser, &insertion);
+                    } else {
+                        ((AstArrayType *) new)->count_expr = parse_expression(parser, 0);
+                    }
+                }
+
+                expect_token(parser, ']');
+                *next_insertion = (AstType *) new;
+                next_insertion = &((AstSliceType *) new)->elem;
+                break;
+            }
+
+            case '$': {
+                parse_polymorphic_variable(parser, &next_insertion);
+                break;
+            }
+
+            case Token_Type_Symbol: {
+                AstNode* symbol_node = make_node(AstNode, Ast_Kind_Symbol);
+                symbol_node->token = expect_token(parser, Token_Type_Symbol);
+
+                *next_insertion = (AstType *) symbol_node;
+
+                while (consume_token_if_next(parser, '.')) {
+                    AstFieldAccess* field = make_node(AstFieldAccess, Ast_Kind_Field_Access);
+                    field->token = expect_token(parser, Token_Type_Symbol);
+                    field->expr  = (AstTyped *) *next_insertion;
+
+                    *next_insertion = (AstType *) field;
+                }
+
+                if (parser->curr->type == '(' && parser->parse_calls) {
+                    OnyxToken* paren_token = expect_token(parser, '(');
+
+                    bh_arr(AstNode *) params = NULL;
+                    bh_arr_new(global_heap_allocator, params, 2);
+
+                    while (!consume_token_if_next(parser, ')')) {
+                        if (parser->hit_unexpected_token) break;
+
+                        AstNode* t = (AstNode *) parse_expression(parser, 0);
+                        bh_arr_push(params, t);
+
+                        if (parser->curr->type != ')')
+                            expect_token(parser, ',');
+                    }
+
+                    AstPolyCallType* pc_type = make_node(AstPolyCallType, Ast_Kind_Poly_Call_Type);
+                    pc_type->token = paren_token;
+                    pc_type->callee = *next_insertion;
+                    pc_type->params = params;
+
+                    *next_insertion = (AstType *) pc_type;
+                }
+
+                next_insertion = NULL;
+                break;
+            }
+
+            case Token_Type_Keyword_Struct: {
+                AstStructType* s_node = parse_struct(parser);
+                *next_insertion = (AstType *) s_node;
+                next_insertion = NULL;
+                break;
+            }
+
+            //
+            // I don't think any of these cases are necesary any more?
+            case Token_Type_Literal_Integer:
+            case Token_Type_Literal_String:
+            case Token_Type_Literal_Float:
+            case Token_Type_Literal_True:
+            case Token_Type_Literal_False:
+            case '-': {
+                *next_insertion = (AstType *) parse_expression(parser, 0);
+                next_insertion = NULL;
+                break;
+            }
+
+            case '(': {
+                OnyxToken* matching = find_matching_paren(parser->curr);
+
+                // :LinearTokenDependent
+                if ((matching + 1)->type == Token_Type_Right_Arrow) {
+                    *next_insertion = parse_function_type(parser, parser->curr);
+
+                } else {
+                    expect_token(parser, '(');
+                    *next_insertion = parse_compound_type(parser);
+                    expect_token(parser, ')');
+                }
+
+                next_insertion = NULL;
+                break;
+            }
+
+            case Token_Type_Keyword_Typeof: {
+                *next_insertion = (AstType *) parse_typeof(parser);
+                next_insertion = NULL;
+                break;
+            }
+
+            default:
+                onyx_report_error(parser->curr->pos, Error_Critical, "unexpected token '%b'.", parser->curr->text, parser->curr->length);
+                consume_token(parser);
+                break;
+        }
+
+        if (next_insertion == NULL) break;
+    }
+
+    return root;
+}
+
+static AstTypeOf* parse_typeof(OnyxParser* parser) {
+    OnyxToken* token = expect_token(parser, Token_Type_Keyword_Typeof);
+
+    AstTypeOf* type_of = make_node(AstTypeOf, Ast_Kind_Typeof);
+    type_of->token = token;
+    type_of->expr = parse_expression(parser, 0);
+    type_of->resolved_type = NULL;
+
+    return type_of;
+}
+
+static void struct_type_create_scope(OnyxParser *parser, AstStructType *s_node) {
+    if (!s_node->scope) {
+        s_node->scope = scope_create(context.ast_alloc, parser->current_scope, s_node->token->pos);
+        parser->current_scope = s_node->scope;
+
+        OnyxToken* current_symbol = bh_arr_last(parser->current_symbol_stack);
+        s_node->scope->name = bh_aprintf(global_heap_allocator, "%b", current_symbol->text, current_symbol->length);
+    }
+}
+
+static AstStructType* parse_struct(OnyxParser* parser) {
+    OnyxToken *s_token = expect_token(parser, Token_Type_Keyword_Struct);
+
+    AstStructType* s_node;
+    AstPolyStructType* poly_struct = NULL;
+
+    s_node = make_node(AstStructType, Ast_Kind_Struct_Type);
+    s_node->token = s_token;
+
+    flush_stored_tags(parser, &s_node->meta_tags);
+
+    // Parse polymorphic parameters
+    if (consume_token_if_next(parser, '(')) {
+        bh_arr(AstPolyStructParam) poly_params = NULL;
+        bh_arr_new(global_heap_allocator, poly_params, 1);
+
+        while (!consume_token_if_next(parser, ')')) {
+            if (parser->hit_unexpected_token) return NULL;
+
+            OnyxToken* sym_token = expect_token(parser, Token_Type_Symbol);
+            expect_token(parser, ':');
+
+            AstType* param_type = parse_type(parser);
+
+            bh_arr_push(poly_params, ((AstPolyStructParam) {
+                .token = sym_token,
+                .type_node = param_type,
+                .type = NULL,
+            }));
+
+            if (parser->curr->type != ')')
+                expect_token(parser, ',');
+        }
+
+        poly_struct = make_node(AstPolyStructType, Ast_Kind_Poly_Struct_Type);
+        poly_struct->token = s_token;
+        poly_struct->poly_params = poly_params;
+        poly_struct->base_struct = s_node;
+    }
+
+    // Parse constraints clause
+    if (parser->curr->type == Token_Type_Keyword_Where) {
+        parse_constraints(parser, &s_node->constraints);
+    }
+
+    bh_arr_new(global_heap_allocator, s_node->members, 4);
+
+    // Parse directives
+    while (parser->curr->type == '#') {
+        if (parser->hit_unexpected_token) return NULL;
+
+        if (parse_possible_directive(parser, "union")) s_node->is_union = 1;
+
+        else if (parse_possible_directive(parser, "pack")) s_node->is_packed = 1;
+
+        else if (parse_possible_directive(parser, "align")) {
+            s_node->min_alignment_ = parse_expression(parser, 0);
+        }
+
+        else if (parse_possible_directive(parser, "size")) {
+            s_node->min_size_ = parse_expression(parser, 0);
+        }
+
+        else {
+            OnyxToken* directive_token = expect_token(parser, '#');
+            OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
+
+            onyx_report_error(directive_token->pos, Error_Critical, "unknown directive '#%b'.", symbol_token->text, symbol_token->length);
+        }
+    }
+
+    expect_token(parser, '{');
+
+    b32 member_is_used = 0;
+    bh_arr(OnyxToken *) member_list_temp = NULL;
+    bh_arr_new(global_heap_allocator, member_list_temp, 4);
+
+    while (!consume_token_if_next(parser, '}')) {
+        if (parser->hit_unexpected_token) return s_node;
+
+        if (parse_possible_directive(parser, "persist")) {
+            struct_type_create_scope(parser, s_node);
+            
+            b32 thread_local = parse_possible_directive(parser, "thread_local");
+
+            OnyxToken* symbol = expect_token(parser, Token_Type_Symbol);
+            AstMemRes* memres = parse_memory_reservation(parser, symbol, thread_local);
+            consume_token_if_next(parser, ';');
+
+            AstBinding* binding = make_node(AstBinding, Ast_Kind_Binding);
+            binding->token = memres->token;
+            binding->node = (AstNode *) memres;
+            ENTITY_SUBMIT(binding);
+            continue;
+        }
+
+        if (next_tokens_are(parser, 3, Token_Type_Symbol, ':', ':')) {
+            struct_type_create_scope(parser, s_node);
+
+            OnyxToken* binding_name = expect_token(parser, Token_Type_Symbol);
+            consume_token(parser);
+
+            AstBinding* binding = parse_top_level_binding(parser, binding_name);
+            if (binding) ENTITY_SUBMIT(binding);
+
+            consume_token_if_next(parser, ';');
+            continue;
+        }
+
+        bh_arr(AstTyped *) meta_tags=NULL;
+        while (parse_possible_directive(parser, "tag")) {
+            if (meta_tags == NULL) bh_arr_new(global_heap_allocator, meta_tags, 1);
+
+            parser->tag_depth += 1;
+
+            do {
+                AstTyped* expr = parse_expression(parser, 0);
+                bh_arr_push(meta_tags, expr);
+            } while (consume_token_if_next(parser, ','));
+
+            parser->tag_depth -= 1;
+        }
+
+        member_is_used = consume_token_if_next(parser, Token_Type_Keyword_Use);
+
+        bh_arr_clear(member_list_temp);
+        while (!consume_token_if_next(parser, ':')) {
+            if (parser->hit_unexpected_token) return NULL;
+            bh_arr_push(member_list_temp, expect_token(parser, Token_Type_Symbol));
+
+            if (parser->curr->type != ':')
+                expect_token(parser, ',');
+        }
+
+        AstType* member_type = NULL;
+        if (parser->curr->type != '=')
+            member_type = parse_type(parser);
+
+        AstTyped* initial_value = NULL;
+        if (consume_token_if_next(parser, '='))
+            initial_value = parse_expression(parser, 0);
+
+        // RECONSIDER: There are seamingly arbitrary limitations put in place here which do two things:
+        //   1. Prevent multiple struct members being used in the same declaration.
+        //      This makes sense because the members will be of the same type, which means
+        //      they have the same members. Using both of the members would immediately result
+        //      in name collisions.
+        //
+        //   2. Prevent multiple struct members having an initializer set for them.
+        //      I think the semantics could be confusing either way, so I'm deciding to leave
+        //      them out of discussion for now. Initialized members should be treated special and
+        //      deserve their own line.
+        if (bh_arr_length(member_list_temp) > 1) {
+            if (member_is_used) onyx_report_error((member_list_temp[0] - 1)->pos, Error_Critical, "'use' is only allowed for a single struct member declaration. Try splitting this compound declaration into multiple lines.");
+            if (initial_value)  onyx_report_error(initial_value->token->pos, Error_Critical, "Intialized values are only allowed on single struct member declarations. Try splitting this compound initializer into multiple lines.");
+        }
+
+        bh_arr_each(OnyxToken *, member_name, member_list_temp) {
+            AstStructMember* mem = make_node(AstStructMember, Ast_Kind_Struct_Member);
+            mem->token = *member_name;
+            mem->type_node = member_type;
+            mem->initial_value = initial_value;
+            mem->meta_tags = meta_tags;
+
+            if (member_is_used) mem->is_used = 1;
+
+            bh_arr_push(s_node->members, mem);
+        }
+
+        if (peek_token(0)->type != '}') {
+            expect_token(parser, ';');
+        }
+    }
+
+    if (s_node->scope) parser->current_scope = parser->current_scope->parent;
+
+    bh_arr_free(member_list_temp);
+
+    if (poly_struct != NULL) {
+        // NOTE: Not a StructType
+        return (AstStructType *) poly_struct;
+
+    } else {
+        ENTITY_SUBMIT(s_node);
+        return s_node;
+    }
+}
+
+static AstInterface* parse_interface(OnyxParser* parser) {
+    AstInterface *interface = make_node(AstInterface, Ast_Kind_Interface);
+    interface->token = expect_token(parser, Token_Type_Keyword_Interface);
+
+    bh_arr_new(global_heap_allocator, interface->params, 2);
+
+    expect_token(parser, '(');
+    while (!consume_token_if_next(parser, ')')) {
+        if (parser->hit_unexpected_token) return interface;
+
+        InterfaceParam ip;
+        ip.value_token = expect_token(parser, Token_Type_Symbol);
+        expect_token(parser, ':');
+        expect_token(parser, '$');
+        ip.type_token  = expect_token(parser, Token_Type_Symbol);
+
+        bh_arr_push(interface->params, ip);
+
+        if (parser->curr->type != ')')
+            expect_token(parser, ',');
+    }
+
+    bh_arr_new(global_heap_allocator, interface->exprs, 2);
+
+    expect_token(parser, '{');
+    while (!consume_token_if_next(parser, '}')) {
+        if (parser->hit_unexpected_token) return interface;
+
+        InterfaceConstraint ic = {0};
+        if (parse_possible_directive(parser, "not")) {
+            ic.invert_condition = 1;
+        }
+
+        if (consume_token_if_next(parser, '{')) {
+            ic.expr = parse_expression(parser, 0);
+
+            expect_token(parser, '}');
+            expect_token(parser, Token_Type_Right_Arrow);
+
+            ic.expected_type_expr = parse_type(parser);
+
+        } else {
+            ic.expr = parse_expression(parser, 0);
+        }
+
+        bh_arr_push(interface->exprs, ic);
+
+        expect_token(parser, ';');
+    }
+
+    return interface;
+}
+
+static AstConstraint* parse_constraint(OnyxParser* parser) {
+    AstConstraint* constraint = make_node(AstConstraint, Ast_Kind_Constraint);
+
+    parser->parse_calls = 0;
+    constraint->interface = (AstInterface *) parse_factor(parser);
+    parser->parse_calls = 1;
+
+    constraint->token = constraint->interface->token;
+
+    bh_arr_new(global_heap_allocator, constraint->type_args, 2);
+
+    expect_token(parser, '(');
+    while (!consume_token_if_next(parser, ')')) {
+        if (parser->hit_unexpected_token) return constraint;
+
+        AstType* type_node = parse_type(parser);
+        bh_arr_push(constraint->type_args, type_node);
+
+        if (parser->curr->type != ')')
+            expect_token(parser, ',');
+    }
+
+    return constraint;
+}
+
+static void parse_constraints(OnyxParser* parser, ConstraintContext *out_constraints) {
+    bh_arr_new(global_heap_allocator, out_constraints->constraints, 2);
+
+    expect_token(parser, Token_Type_Keyword_Where);
+
+    do {
+        AstConstraint *constraint = parse_constraint(parser);
+        if (parser->hit_unexpected_token) return;
+
+        bh_arr_push(out_constraints->constraints, constraint);
+    } while (consume_token_if_next(parser, ','));
+}
+
+static void parse_function_params(OnyxParser* parser, AstFunction* func) {
+    expect_token(parser, '(');
+
+    if (consume_token_if_next(parser, ')')) return;
+
+    u32 param_idx = 0;
+    assert(parser->polymorph_context.poly_params != NULL);
+
+    bh_arr(AstParam) param_buffer=NULL;
+    bh_arr_new(global_heap_allocator, param_buffer, 2);
+
+    OnyxToken* symbol;
+    while (!consume_token_if_next(parser, ')')) {
+        do {
+            if (parser->hit_unexpected_token) return;
+
+            b32 param_use = 0;
+            b32 param_is_baked = 0;
+            AstParam curr_param = { 0 };
+
+            if (consume_token_if_next(parser, Token_Type_Keyword_Use)) param_use = 1;
+            if (consume_token_if_next(parser, '$'))                    param_is_baked = 1;
+
+            symbol = expect_token(parser, Token_Type_Symbol);
+
+            curr_param.vararg_kind = VA_Kind_Not_VA;
+            curr_param.local = make_local(parser->allocator, symbol, NULL);
+            curr_param.local->kind = Ast_Kind_Param;
+
+            if (param_use) {
+                curr_param.is_used = 1;
+                param_use = 0;
+            }
+
+            if (param_is_baked) {
+                curr_param.is_baked = 1;
+                param_is_baked = 0;
+            }
+
+            bh_arr_push(param_buffer, curr_param);
+        } while (consume_token_if_next(parser, ','));
+
+        expect_token(parser, ':');
+
+        VarArgKind vararg_kind=VA_Kind_Not_VA;
+        AstType* type_node=NULL;
+        AstTyped* default_value=NULL;
+
+        if (parser->curr->type != '=') {
+            if (consume_token_if_next(parser, Token_Type_Dot_Dot)) {
+                if (consume_token_if_next(parser, '.')) vararg_kind = VA_Kind_Untyped;
+                else                                    vararg_kind = VA_Kind_Typed;
+            }
+
+            if (vararg_kind != VA_Kind_Untyped) {
+                // CLEANUP: This is mess and it is hard to follow what is going on here.
+                // I think with recent rewrites, this should be easier to do.
+                i32 old_len = bh_arr_length(*parser->polymorph_context.poly_params);
+                type_node = parse_type(parser);
+                i32 new_len = bh_arr_length(*parser->polymorph_context.poly_params);
+
+                if (vararg_kind == VA_Kind_Typed) {
+                    AstVarArgType* va_type = make_node(AstVarArgType, Ast_Kind_VarArg_Type);
+                    va_type->elem = type_node;
+                    va_type->token = type_node->token;
+                    type_node = (AstType *) va_type;
+                }
+
+                fori (i, 0, new_len - old_len) {
+                    (*parser->polymorph_context.poly_params)[old_len + i].type_expr = type_node;
+                    (*parser->polymorph_context.poly_params)[old_len + i].idx = param_idx;
+                }
+            }
+        }
+
+        if (vararg_kind == VA_Kind_Not_VA && consume_token_if_next(parser, '=')) {
+            OnyxToken* directive_token = parser->curr;
+
+            // :Callsite  currently #callsite is only valid as a default value for a funciton parameter.
+            if (parse_possible_directive(parser, "callsite")) {
+                AstCallSite* cs = make_node(AstCallSite, Ast_Kind_Call_Site);
+                cs->token = directive_token;
+                default_value = (AstTyped *) cs;
+
+            } else {
+                default_value = parse_expression(parser, 0);
+            }
+        }
+
+        bh_arr_each(AstParam, param, param_buffer) {
+            param->vararg_kind      = vararg_kind;
+            param->local->type_node = type_node;
+            param->default_value    = default_value;
+
+            if (param->is_baked) {
+                bh_arr(AstPolyParam) pv = *parser->polymorph_context.poly_params;
+                bh_arr_push(pv, ((AstPolyParam) {
+                    .kind = PPK_Baked_Value,
+                    .idx = param_idx,
+
+                    .poly_sym = (AstNode *) param->local,
+                    .type_expr = type_node,
+                }));
+
+                *parser->polymorph_context.poly_params = pv;
+            }
+
+            bh_arr_push(func->params, *param);
+            param_idx++;
+        }
+
+        bh_arr_clear(param_buffer);
+
+        if (parser->curr->type != ')')
+            expect_token(parser, ',');
+    }
+
+    bh_arr_free(param_buffer);
+    return;
+}
+
+static AstOverloadedFunction* parse_overloaded_function(OnyxParser* parser, OnyxToken* token) {
+    b32 locked = 0;
+    if (parse_possible_directive(parser, "locked")) {
+        locked = 1;
+    }
+
+    b32 local = 0;
+    if (parse_possible_directive(parser, "local")) {
+        local = 1;
+    }
+
+    // This could be checked elsewhere?
+    if (locked && local) {
+        onyx_report_error(token->pos, Error_Critical, "Only one of '#locked' and '#local' can because use at a time.");
+    }
+
+    expect_token(parser, '{');
+
+    AstOverloadedFunction* ofunc = make_node(AstOverloadedFunction, Ast_Kind_Overloaded_Function);
+    ofunc->token = token;
+    ofunc->flags |= Ast_Flag_Comptime;
+    ofunc->locked = locked;
+    ofunc->only_local_functions = local;
+
+    bh_arr_new(global_heap_allocator, ofunc->overloads, 4);
+
+    u64 precedence = 0;
+    while (!consume_token_if_next(parser, '}')) {
+        if (parser->hit_unexpected_token) return ofunc;
+
+        if (parse_possible_directive(parser, "precedence")) {
+            AstNumLit* pre = parse_int_literal(parser);
+            if (parser->hit_unexpected_token) return ofunc;
+
+            precedence = bh_max(pre->value.l, 0);
+        }
+
+        AstTyped* option = parse_expression(parser, 0);
+        add_overload_option(&ofunc->overloads, precedence++, option);
+
+        if (parser->curr->type != '}')
+            expect_token(parser, ',');
+    }
+
+    ENTITY_SUBMIT(ofunc);
+    return ofunc;
+}
+
+static AstFunction* parse_function_definition(OnyxParser* parser, OnyxToken* token) {
+    AstFunction* func_def = make_node(AstFunction, Ast_Kind_Function);
+    func_def->token = token;
+    bh_arr_push(parser->current_function_stack, func_def);
+
+    flush_stored_tags(parser, &func_def->tags);
+
+    bh_arr_new(global_heap_allocator, func_def->params, 4);
+
+    bh_arr(AstPolyParam) polymorphic_vars = NULL;
+    bh_arr_new(global_heap_allocator, polymorphic_vars, 4);
+    // defer bh_arr_free(polymorphic_vars);
+
+    parser->polymorph_context.poly_params = &polymorphic_vars;
+    parse_function_params(parser, func_def);
+    parser->polymorph_context.poly_params = NULL;
+
+    func_def->return_type = (AstType *) &basic_type_void;
+
+    char* name = NULL;
+    if (bh_arr_length(parser->current_symbol_stack) > 0) {
+        OnyxToken *current_symbol = bh_arr_last(parser->current_symbol_stack);
+        name = bh_aprintf(global_heap_allocator, "%b", current_symbol->text, current_symbol->length);
+    }
+
+    if (consume_token_if_next(parser, '=')) {
+        expect_token(parser, '>');
+        func_def->return_type = (AstType *) &basic_type_auto_return;
+
+        if (parser->curr->type == '{') {
+            func_def->body = parse_block(parser, 1, name);
+
+        } else {
+            AstTyped* returned_value = parse_compound_expression(parser, 0);
+            if (returned_value == NULL) goto function_defined;
+
+            AstReturn* return_node = make_node(AstReturn, Ast_Kind_Return);
+            return_node->token = returned_value->token;
+            return_node->expr = returned_value;
+
+            AstBlock* body_block = make_node(AstBlock, Ast_Kind_Block);
+            body_block->token = returned_value->token;
+            body_block->body = (AstNode *) return_node;
+
+            func_def->body = body_block;
+        }
+
+        goto function_defined;
+    }
+
+    if (consume_token_if_next(parser, Token_Type_Right_Arrow)) {
+        if (parse_possible_directive(parser, "auto")) {
+            func_def->return_type = (AstType *) &basic_type_auto_return;
+        } else {
+            func_def->return_type = parse_type(parser);
+        }
+    }
+
+    if (parser->curr->type == Token_Type_Keyword_Where) {
+        parse_constraints(parser, &func_def->constraints);
+    }
+
+    while (parser->curr->type == '#') {
+        if (parse_possible_directive(parser, "intrinsic")) {
+            func_def->is_intrinsic = 1;
+
+            if (parser->curr->type == Token_Type_Literal_String) {
+                func_def->intrinsic_name = expect_token(parser, Token_Type_Literal_String);
+            }
+        }
+
+        else if (parse_possible_directive(parser, "foreign")) {
+            func_def->foreign_module = expect_token(parser, Token_Type_Literal_String);
+            func_def->foreign_name   = expect_token(parser, Token_Type_Literal_String);
+
+            func_def->is_foreign = 1;
+        }
+
+        // HACK: NullProcHack
+        else if (parse_possible_directive(parser, "null")) {
+            func_def->flags |= Ast_Flag_Proc_Is_Null;
+        }
+
+        else {
+            OnyxToken* directive_token = expect_token(parser, '#');
+            OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
+
+            onyx_report_error(directive_token->pos, Error_Critical, "unknown directive '#%b'.", symbol_token->text, symbol_token->length);
+        }
+    }
+
+    func_def->body = parse_block(parser, 1, name);
+
+    // :LinearTokenDependent
+    func_def->closing_brace = parser->curr - 1;
+
+function_defined:
+    if (bh_arr_length(polymorphic_vars) > 0) {
+        func_def->kind = Ast_Kind_Polymorphic_Proc;
+        func_def->poly_params = polymorphic_vars;
+
+    } else {
+        bh_arr_free(polymorphic_vars);
+    }
+
+    bh_arr_pop(parser->current_function_stack);
+    return func_def;
+}
+
+static b32 parse_possible_function_definition_no_consume(OnyxParser* parser) {
+    if (parser->curr->type == '(') {
+        OnyxToken* matching_paren = find_matching_paren(parser->curr);
+        if (matching_paren == NULL) return 0;
+
+        if (next_tokens_are(parser, 4, '(', ')', '=', '>')) return 0;
+
+        // :LinearTokenDependent
+        OnyxToken* token_after_paren = matching_paren + 1;
+        if (token_after_paren->type != Token_Type_Right_Arrow
+            && token_after_paren->type != '{'
+            && token_after_paren->type != Token_Type_Keyword_Do
+            && token_after_paren->type != Token_Type_Empty_Block
+            && token_after_paren->type != Token_Type_Keyword_Where
+            && (token_after_paren->type != '=' || (token_after_paren + 1)->type != '>'))
+            return 0;
+
+        // :LinearTokenDependent
+        b32 is_params = (parser->curr + 1) == matching_paren;
+        OnyxToken* tmp_token = parser->curr;
+        while (!is_params && tmp_token < matching_paren) {
+            if (tmp_token->type == ':') is_params = 1;
+
+            tmp_token++;
+        }
+
+        if (!is_params) return 0;
+
+        return 1;
+    }
+
+    return 0;
+}
+
+static b32 parse_possible_function_definition(OnyxParser* parser, AstTyped** ret) {
+    if (parse_possible_function_definition_no_consume(parser)) {
+        OnyxToken* proc_token = parser->curr;
+        AstFunction* func_node = parse_function_definition(parser, proc_token);
+        *ret = (AstTyped *) func_node;
+
+        return 1;
+    }
+
+    return 0;
+}
+
+typedef struct QuickParam {
+    OnyxToken* token;
+    b32 is_baked;
+} QuickParam;
+
+static b32 parse_possible_quick_function_definition_no_consume(OnyxParser* parser) {
+    //
+    // x => x + 1 case.
+    if (next_tokens_are(parser, 3, Token_Type_Symbol, '=', '>')) {
+        return 1;
+    }
+
+    if (parser->curr->type != '(') return 0;
+
+    OnyxToken* matching_paren = find_matching_paren(parser->curr);
+    if (matching_paren == NULL) return 0;
+
+    // :LinearTokenDependent
+    OnyxToken* token_after_paren = matching_paren + 1;
+    if (token_after_paren->type != '=' || (token_after_paren + 1)->type != '>')
+        return 0;
+
+    return 1;
+}
+
+static b32 parse_possible_quick_function_definition(OnyxParser* parser, AstTyped** ret) {
+    if (!parse_possible_quick_function_definition_no_consume(parser)) return 0;
+
+    bh_arr(QuickParam) params=NULL;
+    bh_arr_new(global_heap_allocator, params, 4);
+    OnyxToken* proc_token;
+
+    if (parser->curr->type == Token_Type_Symbol) {
+        QuickParam param = { 0 };
+        param.token = expect_token(parser, Token_Type_Symbol);
+        proc_token = param.token;
+        bh_arr_push(params, param);
+
+    } else {
+        proc_token = expect_token(parser, '(');
+        while (parser->curr->type != ')') {
+            if (parser->hit_unexpected_token) return 0;
+
+            QuickParam param = { 0 };
+            if (consume_token_if_next(parser, '$')) param.is_baked = 1;
+            param.token = expect_token(parser, Token_Type_Symbol);
+
+            bh_arr_push(params, param);
+
+            if (parser->curr->type != ')') {
+                expect_token(parser, ',');
+            }
+        }
+
+        expect_token(parser, ')');
+    }
+
+    expect_token(parser, '=');
+    expect_token(parser, '>');
+
+    bh_arr(AstNode *) poly_params=NULL;
+    bh_arr_new(global_heap_allocator, poly_params, bh_arr_length(params));
+    bh_arr_each(QuickParam, param, params) {
+        char text[512];
+        memset(text, 0, 512);
+        strncat(text, "__type_", 511);
+        token_toggle_end(param->token);
+        strncat(text, param->token->text, 511);
+        token_toggle_end(param->token);
+
+        OnyxToken* new_token = bh_alloc(parser->allocator, sizeof(OnyxToken));
+        new_token->type = Token_Type_Symbol;
+        new_token->length = 7 + param->token->length;
+        new_token->text = bh_strdup(parser->allocator, text);
+        new_token->pos = param->token->pos;
+
+        AstNode* type_node = make_symbol(parser->allocator, new_token);
+        bh_arr_push(poly_params, type_node);
+    }
+
+    AstFunction* poly_proc = make_node(AstFunction, Ast_Kind_Polymorphic_Proc);
+
+    bh_arr_new(global_heap_allocator, poly_proc->params, bh_arr_length(params));
+    fori (i, 0, bh_arr_length(params)) {
+        AstLocal* param_local = make_local(parser->allocator, params[i].token, (AstType *) poly_params[i]);
+        param_local->kind = Ast_Kind_Param;
+
+        bh_arr_push(poly_proc->params, ((AstParam) {
+            .local = param_local,
+            .default_value = NULL,
+
+            .vararg_kind = 0,
+            .use_processed = 0,
+        }));
+    }
+
+    AstBlock* body_block;
+    AstType*  return_type;
+    bh_arr_push(parser->current_function_stack, poly_proc);
+
+    if (parser->curr->type == '{') {
+        char* name = NULL;
+        if (bh_arr_length(parser->current_symbol_stack) > 0) {
+            OnyxToken *current_symbol = bh_arr_last(parser->current_symbol_stack);
+            name = bh_aprintf(global_heap_allocator, "%b", current_symbol->text, current_symbol->length);
+        }
+
+        body_block = parse_block(parser, 1, name);
+        return_type = (AstType *) &basic_type_auto_return;
+
+    } else {
+        AstTyped* body = parse_compound_expression(parser, 0);
+
+        AstReturn* return_node = make_node(AstReturn, Ast_Kind_Return);
+        return_node->token = body->token;
+        return_node->expr = body;
+
+        body_block = make_node(AstBlock, Ast_Kind_Block);
+        body_block->token = body->token;
+        body_block->body = (AstNode *) return_node;
+
+        AstTypeOf* return_type_of = make_node(AstTypeOf, Ast_Kind_Typeof);
+        return_type_of->token = body->token;
+        return_type_of->expr = body;
+        return_type = (AstType *) return_type_of;
+    }
+
+    poly_proc->token = proc_token;
+    poly_proc->body = body_block;
+    poly_proc->return_type = (AstType *) return_type;
+
+    poly_proc->token = proc_token;
+    bh_arr_new(global_heap_allocator, poly_proc->poly_params, bh_arr_length(params));
+    fori (i, 0, bh_arr_length(params)) {
+        bh_arr_push(poly_proc->poly_params, ((AstPolyParam) {
+            .kind = PSK_Type,
+            .idx  = i,
+            .poly_sym = poly_params[i],
+            .type_expr = (AstType *) poly_params[i],
+            .type = NULL,
+        }));
+
+        if (params[i].is_baked) {
+            // This is not handled currently, as you cannot say f :: ($x: $T) yet, which is what this would have to do.
+        }
+    }
+
+    *ret = (AstTyped *) poly_proc;
+
+    bh_arr_pop(parser->current_function_stack);
+    bh_arr_free(params);
+    bh_arr_free(poly_params);
+    return 1;
+}
+
+static AstTyped* parse_global_declaration(OnyxParser* parser) {
+    expect_no_stored_tags(parser);
+
+    AstGlobal* global_node = make_node(AstGlobal, Ast_Kind_Global);
+    global_node->token = expect_token(parser, Token_Type_Keyword_Global);
+
+    global_node->type_node = parse_type(parser);
+
+    ENTITY_SUBMIT(global_node);
+
+    return (AstTyped *) global_node;
+}
+
+static AstEnumType* parse_enum_declaration(OnyxParser* parser) {
+    expect_no_stored_tags(parser);
+
+    AstEnumType* enum_node = make_node(AstEnumType, Ast_Kind_Enum_Type);
+    enum_node->token = expect_token(parser, Token_Type_Keyword_Enum);
+
+    bh_arr_new(global_heap_allocator, enum_node->values, 4);
+
+    while (parser->curr->type == '#') {
+        if (parser->hit_unexpected_token) return enum_node;
+
+        if (parse_possible_directive(parser, "flags")) {
+            enum_node->is_flags = 1;
+        } else {
+            OnyxToken* directive_token = expect_token(parser, '#');
+            OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
+
+            onyx_report_error(directive_token->pos, Error_Critical, "unknown directive '#%b'.", symbol_token->text, symbol_token->length);
+        }
+    }
+
+    AstType* backing = (AstType *) &basic_type_u32;
+    if (consume_token_if_next(parser, '(')) {
+        AstNode* backing_sym = make_node(AstNode, Ast_Kind_Symbol);
+        backing_sym->token = expect_token(parser, Token_Type_Symbol);
+        backing = (AstType *) backing_sym;
+
+        expect_token(parser, ')');
+    }
+    enum_node->backing = backing;
+
+    expect_token(parser, '{');
+
+    while (!consume_token_if_next(parser, '}')) {
+        if (parser->hit_unexpected_token) return enum_node;
+
+        AstEnumValue* evalue = make_node(AstEnumValue, Ast_Kind_Enum_Value);
+        evalue->token = expect_token(parser, Token_Type_Symbol);
+        evalue->type_node = (AstType *) enum_node;
+
+        if (consume_token_if_next(parser, ':')) {
+            expect_token(parser, ':');
+            evalue->value = parse_expression(parser, 0);
+        }
+
+        expect_token(parser, ';');
+
+        bh_arr_push(enum_node->values, evalue);
+    }
+
+    return enum_node;
+}
+
+static AstIf* parse_static_if_stmt(OnyxParser* parser, b32 parse_block_as_statements) {
+    expect_no_stored_tags(parser);
+
+    AstIf* static_if_node = make_node(AstIf, Ast_Kind_Static_If);
+    static_if_node->token = expect_token(parser, '#');
+    static_if_node->defined_in_scope = parser->current_scope;
+    expect_token(parser, Token_Type_Keyword_If);
+
+    static_if_node->cond = parse_expression(parser, 0);
+
+    bh_arr_new(global_heap_allocator, static_if_node->true_entities, 2);
+    bh_arr_push(parser->alternate_entity_placement_stack, &static_if_node->true_entities);
+
+    if (parse_block_as_statements) {
+        static_if_node->true_stmt = parse_block(parser, 0, NULL);
+
+    } else {
+        expect_token(parser, '{');
+        while (!consume_token_if_next(parser, '}')) {
+            if (parser->hit_unexpected_token) return static_if_node;
+
+            parse_top_level_statement(parser);
+        }
+    }
+
+    bh_arr_pop(parser->alternate_entity_placement_stack);
+
+    if (consume_token_if_next(parser, Token_Type_Keyword_Else)) {
+        bh_arr_new(global_heap_allocator, static_if_node->false_entities, 2);
+        bh_arr_push(parser->alternate_entity_placement_stack, &static_if_node->false_entities);
+
+        if (parse_block_as_statements) {
+            static_if_node->false_stmt = parse_block(parser, 0, NULL);
+
+        } else {
+            expect_token(parser, '{');
+            while (!consume_token_if_next(parser, '}')) {
+                if (parser->hit_unexpected_token) return static_if_node;
+
+                parse_top_level_statement(parser);
+            }
+        }
+
+        bh_arr_pop(parser->alternate_entity_placement_stack);
+    }
+
+    return static_if_node;
+}
+
+static AstMemRes* parse_memory_reservation(OnyxParser* parser, OnyxToken* symbol, b32 threadlocal) {
+    expect_token(parser, ':');
+
+    expect_no_stored_tags(parser);
+
+    AstMemRes* memres = make_node(AstMemRes, Ast_Kind_Memres);
+    memres->threadlocal = threadlocal;
+    memres->token = symbol;
+
+    if (parser->curr->type != '=')
+        memres->type_node = parse_type(parser);
+
+    if (consume_token_if_next(parser, '='))
+        memres->initial_value = parse_expression(parser, 1);
+
+    ENTITY_SUBMIT(memres);
+    return memres;
+}
+
+static AstMacro* parse_macro(OnyxParser* parser) {
+    expect_no_stored_tags(parser);
+
+    AstMacro* macro = make_node(AstMacro, Ast_Kind_Macro);
+    macro->token = expect_token(parser, Token_Type_Keyword_Macro);
+
+    if (parse_possible_function_definition(parser, &macro->body)) {
+        ENTITY_SUBMIT(macro);
+        return macro;
+    }
+
+    if (parse_possible_quick_function_definition(parser, &macro->body)) {
+        ENTITY_SUBMIT(macro);
+        return macro;
+    }
+
+    onyx_report_error(parser->curr->pos, Error_Critical, "'macro' expects to be followed by a producure definition.");
+    return NULL;
+}
+
+static AstDirectiveInit* parse_init_directive(OnyxParser *parser, OnyxToken *token) {
+    AstDirectiveInit *init = make_node(AstDirectiveInit, Ast_Kind_Directive_Init);
+    init->token = token;
+
+    parser->parse_calls = 0;
+    while (parse_possible_directive(parser, "after")) {
+        if (parser->hit_unexpected_token) return init;
+        if (init->dependencies == NULL) bh_arr_new(global_heap_allocator, init->dependencies, 2);
+
+        AstTyped *dependency = parse_expression(parser, 0);
+        bh_arr_push(init->dependencies, (AstDirectiveInit *) dependency);
+    }
+    parser->parse_calls = 1;
+
+    init->init_proc = parse_expression(parser, 0);
+    ENTITY_SUBMIT(init);
+    return init;
+}
+
+static AstForeignBlock* parse_foreign_block(OnyxParser* parser, OnyxToken *token) {
+    // :LinearTokenDependent
+    AstForeignBlock *fb = make_node(AstForeignBlock, Ast_Kind_Foreign_Block);
+    fb->token = token;
+    fb->module_name = expect_token(parser, Token_Type_Literal_String);
+
+    //
+    // This has a fun implication that there cannot be foreign blocks in the builtin
+    // or type_info packages, as those are loaded before foreign_block_type has a value.
+    fb->type_node = foreign_block_type;
+
+    bh_arr_new(global_heap_allocator, fb->captured_entities, 4);
+    bh_arr_push(parser->alternate_entity_placement_stack, &fb->captured_entities);
+
+    expect_token(parser, '{');
+    parse_top_level_statements_until(parser, '}');
+    expect_token(parser, '}');
+
+    bh_arr_pop(parser->alternate_entity_placement_stack);
+    ENTITY_SUBMIT(fb);
+
+    return fb;
+}
+
+static AstTyped* parse_top_level_expression(OnyxParser* parser) {
+    if (parser->curr->type == Token_Type_Keyword_Global)    return parse_global_declaration(parser);
+    if (parser->curr->type == Token_Type_Keyword_Struct)    return (AstTyped *) parse_struct(parser);
+    if (parser->curr->type == Token_Type_Keyword_Interface) return (AstTyped *) parse_interface(parser);
+    if (parser->curr->type == Token_Type_Keyword_Enum)      return (AstTyped *) parse_enum_declaration(parser);
+    if (parser->curr->type == Token_Type_Keyword_Macro)     return (AstTyped *) parse_macro(parser);
+
+    if (parser->curr->type == '#') {
+        if (parse_possible_directive(parser, "type")) {
+            AstTypeAlias* alias = make_node(AstTypeAlias, Ast_Kind_Type_Alias);
+            alias->to = parse_type(parser);
+            return (AstTyped *) alias;
+        }
+
+        if (parse_possible_directive(parser, "match")) {
+            // :LinearTokenDependent
+            OnyxToken* directive_token = parser->curr - 2;
+            AstOverloadedFunction* ofunc = parse_overloaded_function(parser, directive_token);
+            return (AstTyped *) ofunc;
+        }
+
+        if (parse_possible_directive(parser, "init")) {
+            // :LinearTokenDependent
+            AstDirectiveInit *init = parse_init_directive(parser, parser->curr - 2);
+            return (AstTyped *) init;
+        }
+
+        if (parse_possible_directive(parser, "distinct")) {
+            // :LinearTokenDependent
+            AstDistinctType *distinct = make_node(AstDistinctType, Ast_Kind_Distinct_Type);
+            distinct->token = parser->curr - 2;
+            distinct->base_type = parse_type(parser);
+            return (AstTyped *) distinct;
+        }
+
+        if (parse_possible_directive(parser, "foreign")) {
+            AstForeignBlock *foreign = parse_foreign_block(parser, parser->curr - 2);
+            return (AstTyped *) foreign;
+        }
+    }
+
+    return parse_expression(parser, 1);
+}
+
+static char* generate_name_within_scope(OnyxParser* parser, OnyxToken* symbol) {
+    char name[512];
+    memset(name, 0, 512);
+
+    bh_arr(char *) names=NULL;
+    bh_arr_new(global_heap_allocator, names, 4);
+
+    Scope* scope = parser->current_scope;
+    while (scope != NULL) {
+        bh_arr_push(names, scope->name);
+        scope = scope->parent;
+    }
+
+    bh_arr_each(char *, n, names) {
+        if (*n == NULL) continue;
+
+        strncat(name, *n, 511);
+        strncat(name, ".", 511);
+    }
+    bh_arr_free(names);
+
+    return bh_aprintf(global_heap_allocator, "%s%b", name, symbol->text, symbol->length);
+}
+
+static AstBinding* parse_top_level_binding(OnyxParser* parser, OnyxToken* symbol) {
+    OnyxToken *after_second_colon = expect_token(parser, ':');
+    if (after_second_colon) after_second_colon += 1;
+
+    AstTyped* node = parse_top_level_expression(parser);
+    if (parser->hit_unexpected_token || node == NULL)
+        return NULL;
+
+    switch (node->kind) {
+        case Ast_Kind_Function:
+        case Ast_Kind_Polymorphic_Proc: {
+            AstFunction* func = (AstFunction *) node;
+
+            if (func->intrinsic_name == NULL) func->intrinsic_name = symbol;
+
+            func->name = generate_name_within_scope(parser, symbol);
+            break;
+        }
+
+        case Ast_Kind_Macro: {
+            AstMacro* macro = (AstMacro *) node;
+
+            AstFunction* func = (AstFunction *) macro->body;
+            func->name = generate_name_within_scope(parser, symbol);
+            break;
+        }
+
+        case Ast_Kind_Directive_Init: break;
+
+        case Ast_Kind_Global: ((AstGlobal *) node)->name = generate_name_within_scope(parser, symbol);
+
+        case Ast_Kind_Overloaded_Function:
+        case Ast_Kind_StrLit:
+            break;
+
+        case Ast_Kind_Interface:
+        case Ast_Kind_Struct_Type:
+        case Ast_Kind_Poly_Struct_Type:
+        case Ast_Kind_Enum_Type:
+        case Ast_Kind_Distinct_Type:
+            ((AstStructType *) node)->name = generate_name_within_scope(parser, symbol);
+            goto default_case;
+
+        case Ast_Kind_Type_Alias:
+            node->token = symbol;
+            goto default_case;
+
+        case Ast_Kind_Package: goto default_case;
+        case Ast_Kind_NumLit:  goto default_case;
+
+        default: {
+            if (!node_is_type((AstNode *) node)) {
+                AstAlias* alias = make_node(AstAlias, Ast_Kind_Alias);
+                alias->token = node->token;
+                alias->alias = node;
+                node = (AstTyped *) alias;
+            }
+
+default_case:
+            ENTITY_SUBMIT(node);
+        }
+    }
+
+    AstBinding* binding = make_node(AstBinding, Ast_Kind_Binding);
+    binding->token = symbol;
+    binding->node = (AstNode *) node;
+
+    if (after_second_colon) expect_no_stored_tags_pos(parser, after_second_colon->pos);
+    return binding;
+}
+
+static void parse_top_level_statement(OnyxParser* parser) {
+    AstFlags private_kind = 0;
+    if (bh_arr_length(parser->scope_flags) > 0)
+        private_kind = bh_arr_last(parser->scope_flags);
+
+    // :CLEANUP this very repetetive code...
+    if (next_tokens_are(parser, 2, '#', Token_Type_Keyword_Package)) {
+        consume_tokens(parser, 2);
+        private_kind = Ast_Flag_Private_Package;
+        if (parser->curr->type == '{') {
+            bh_arr_push(parser->scope_flags, private_kind);
+
+            expect_token(parser, '{');
+            parse_top_level_statements_until(parser, '}');
+            expect_token(parser, '}');
+
+            bh_arr_pop(parser->scope_flags);
+            return;
+        }
+    }
+    else if (parse_possible_directive(parser, "local")) {
+        private_kind = Ast_Flag_Private_File;
+        if (parser->curr->type == '{') {
+            bh_arr_push(parser->scope_flags, private_kind);
+
+            expect_token(parser, '{');
+            parse_top_level_statements_until(parser, '}');
+            expect_token(parser, '}');
+
+            bh_arr_pop(parser->scope_flags);
+            return;
+        }
+    }
+
+    AstBinding* binding = NULL;
+
+    switch ((u16) parser->curr->type) {
+        case Token_Type_Keyword_Use: {
+            AstNode* use_node = parse_use_stmt(parser);
+            if (use_node) ENTITY_SUBMIT(use_node);
+            return;
+        }
+
+        case Token_Type_Symbol: {
+            OnyxToken* symbol = expect_token(parser, Token_Type_Symbol);
+
+            if (next_tokens_are(parser, 2, ':', ':')) {
+                expect_token(parser, ':');
+
+                bh_arr_push(parser->current_symbol_stack, symbol);
+                binding = parse_top_level_binding(parser, symbol);
+                bh_arr_pop(parser->current_symbol_stack);
+
+                if (binding != NULL) binding->flags |= private_kind;
+
+                goto submit_binding_to_entities;
+            }
+
+            AstMemRes* memres = parse_memory_reservation(parser, symbol, 0);
+
+            binding = make_node(AstBinding, Ast_Kind_Binding);
+            binding->token = symbol;
+            binding->flags |= private_kind;
+            binding->node = (AstNode *) memres;
+
+            goto submit_binding_to_entities;
+        }
+
+        case '(': {
+            AstTyped *retval = NULL;
+            if (parse_possible_function_definition(parser, &retval)) {
+                ENTITY_SUBMIT(retval);
+                return;
+            }
+            if (parse_possible_quick_function_definition(parser, &retval)) {
+                ENTITY_SUBMIT(retval);
+                return;
+            }
+            break;
+        }
+
+        case '#': {
+            if (next_tokens_are(parser, 2, '#', Token_Type_Keyword_If)) {
+                AstIf* static_if = parse_static_if_stmt(parser, 0);
+                ENTITY_SUBMIT(static_if);
+                return;
+            }
+
+            OnyxToken* dir_token = parser->curr;
+
+            if (parse_possible_directive(parser, "load")) {
+                AstInclude* include = make_node(AstInclude, Ast_Kind_Load_File);
+                include->token = dir_token;
+                include->name_node = parse_expression(parser, 0);
+
+                ENTITY_SUBMIT(include);
+                return;
+            }
+            else if (parse_possible_directive(parser, "load_all")) {
+                AstInclude* include = make_node(AstInclude, Ast_Kind_Load_All);
+                include->token = dir_token;
+                include->name_node = parse_expression(parser, 0);
+
+                ENTITY_SUBMIT(include);
+                return;
+            }
+            else if (parse_possible_directive(parser, "load_path")) {
+                AstInclude* include = make_node(AstInclude, Ast_Kind_Load_Path);
+                include->token = dir_token;
+                include->name_node = parse_expression(parser, 0);
+
+                ENTITY_SUBMIT(include);
+                return;
+            }
+            else if (parse_possible_directive(parser, "library_path")) {
+                AstInclude* include = make_node(AstInclude, Ast_Kind_Library_Path);
+                include->token = dir_token;
+                include->name_node = parse_expression(parser, 0);
+
+                ENTITY_SUBMIT(include);
+                return;
+            }
+            else if (parse_possible_directive(parser, "error")) {
+                AstDirectiveError *error = make_node(AstDirectiveError, Ast_Kind_Directive_Error);
+                error->token = dir_token;
+                error->error_msg = expect_token(parser, Token_Type_Literal_String);
+
+                ENTITY_SUBMIT(error);
+                return;
+            }
+            else if (parse_possible_directive(parser, "foreign")) {
+                parse_foreign_block(parser, parser->curr - 2);
+                return;
+            }
+            else if (parse_possible_directive(parser, "operator")) {
+                AstDirectiveOperator *operator = make_node(AstDirectiveOperator, Ast_Kind_Directive_Operator);
+                operator->token = dir_token;
+
+                // These cases have to happen first because these are not necessarily "binary operators",
+                // they are just things that I want to be able to overload. []= is technically a ternary
+                // operator so all these things are horribly named anyway.
+                if (next_tokens_are(parser, 3, '^', '[', ']')) {
+                    consume_tokens(parser, 3);
+                    operator->operator = Binary_Op_Ptr_Subscript;
+                    goto operator_determined;
+                }
+
+                if (next_tokens_are(parser, 3, '[', ']', '=')) {
+                    consume_tokens(parser, 3);
+                    operator->operator = Binary_Op_Subscript_Equals;
+                    goto operator_determined;
+                }
+
+                // The default case
+                BinaryOp op = binary_op_from_token_type(parser->curr->type);
+                consume_token(parser);
+                if (op == Binary_Op_Subscript) expect_token(parser, ']');    // #operator [] ... needs to consume the other ']'
+
+                if (op == Binary_Op_Count) {
+                    onyx_report_error(parser->curr->pos, Error_Critical, "Invalid binary operator.");
+                } else {
+                    operator->operator = op;
+                }
+
+              operator_determined:
+                operator->overload = parse_expression(parser, 0);
+
+                ENTITY_SUBMIT(operator);
+                return;
+            }
+            else if (parse_possible_directive(parser, "match") || parse_possible_directive(parser, "overload")) {
+                AstDirectiveAddOverload *add_overload = make_node(AstDirectiveAddOverload, Ast_Kind_Directive_Add_Overload);
+                add_overload->token = dir_token;
+
+                if (parse_possible_directive(parser, "precedence")) {
+                    AstNumLit* pre = parse_int_literal(parser);
+                    if (parser->hit_unexpected_token) return;
+
+                    add_overload->precedence = bh_max(pre->value.l, 0);
+                } else {
+                    add_overload->precedence = 0;
+                }
+
+                parser->parse_calls = 0;
+                add_overload->overloaded_function = (AstNode *) parse_expression(parser, 0);
+                parser->parse_calls = 1;
+
+                // Allow for
+                //      #match
+                //      something :: (....) {
+                //      }
+                // 
+                // This will make converting something to a overloaded
+                // function easier and require less copying by the programmer.
+                if (next_tokens_are(parser, 2, ':', ':')) {
+                    consume_tokens(parser, 2);
+                }
+
+                add_overload->overload = parse_expression(parser, 0);
+
+                ENTITY_SUBMIT(add_overload);
+                return;
+            }
+            else if (parse_possible_directive(parser, "inject")) {
+                AstInjection *inject = make_node(AstInjection, Ast_Kind_Injection);
+                inject->token = dir_token;
+
+                parser->parse_calls = 0;
+                inject->full_loc = parse_expression(parser, 0);
+                parser->parse_calls = 1;
+
+                // See comment above
+                if (next_tokens_are(parser, 2, ':', ':')) {
+                    consume_tokens(parser, 2);
+                }
+
+                inject->to_inject = parse_expression(parser, 0);
+                
+                ENTITY_SUBMIT(inject);
+                return;
+            }
+            else if (parse_possible_directive(parser, "export")) {
+                AstDirectiveExport *export = make_node(AstDirectiveExport, Ast_Kind_Directive_Export);
+                export->token = dir_token;
+                parser->parse_calls = 0;
+                export->export_name_expr = parse_expression(parser, 0); // expect_token(parser, Token_Type_Literal_String);
+                parser->parse_calls = 1;
+
+                export->export = parse_expression(parser, 0);
+
+                ENTITY_SUBMIT(export);
+                return;
+            }
+            else if (parse_possible_directive(parser, "thread_local")) {
+                OnyxToken* symbol = expect_token(parser, Token_Type_Symbol);
+                AstMemRes* memres = parse_memory_reservation(parser, symbol, 1);
+
+                binding = make_node(AstBinding, Ast_Kind_Binding);
+                binding->token = symbol;
+                binding->flags |= private_kind;
+                binding->node = (AstNode *) memres;
+
+                goto submit_binding_to_entities;
+            }
+            else if (parse_possible_directive(parser, "init")) {
+                // :LinearTokenDependent
+                parse_init_directive(parser, parser->curr - 2);
+                return;
+            }
+            else if (parse_possible_directive(parser, "library")) {
+                // :LinearTokenDependent
+                AstDirectiveLibrary *library = make_node(AstDirectiveLibrary, Ast_Kind_Directive_Library);
+                library->token = parser->curr - 2;
+                library->library_symbol = parse_expression(parser, 0);
+
+                ENTITY_SUBMIT(library);
+                return;
+            }
+            else if (parse_possible_directive(parser, "tag")) {
+                parser->tag_depth += 1;
+
+                AstTyped *expr = parse_expression(parser, 0);
+                bh_arr_push(parser->stored_tags, expr);
+
+                parser->tag_depth -= 1;
+                return;
+            }
+            else {
+                OnyxToken* directive_token = expect_token(parser, '#');
+                OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
+
+                onyx_report_error(directive_token->pos, Error_Critical, "unknown directive '#%b'.", symbol_token->text, symbol_token->length);
+                return;
+            }
+        }
+
+        default: break;
+    }
+
+    expect_token(parser, ';');
+    return;
+
+submit_binding_to_entities:
+    {
+        if (!binding) return;
+
+        Scope* target_scope = parser->package->scope;
+
+        if (binding->flags & Ast_Flag_Private_Package)
+            target_scope = parser->package->private_scope;
+        if (binding->flags & Ast_Flag_Private_File)
+            target_scope = parser->file_scope;
+
+        ENTITY_SUBMIT_IN_SCOPE(binding, target_scope);
+    }
+}
+
+static AstPackage* parse_package_expression(OnyxParser* parser) {
+    AstPackage* package_node = make_node(AstPackage, Ast_Kind_Package);
+    package_node->token = expect_token(parser, Token_Type_Keyword_Package);
+
+    bh_arr_new(global_heap_allocator, package_node->path, 2);
+
+    while (parser->curr->type == Token_Type_Symbol) {
+        if (parser->hit_unexpected_token) return package_node;
+
+        OnyxToken* symbol = expect_token(parser, Token_Type_Symbol);
+
+        bh_arr_push(package_node->path, symbol);
+
+        if (consume_token_if_next(parser, '.'));
+        else break;
+    }
+
+    i32 total_package_name_length = 0;
+    bh_arr_each(OnyxToken *, token, package_node->path) {
+        total_package_name_length += (*token)->length + 1;
+    }
+
+    char* package_name = bh_alloc_array(context.ast_alloc, char, total_package_name_length);
+    *package_name = '\0';
+
+    bh_arr_each(OnyxToken *, token, package_node->path) {
+        token_toggle_end(*token);
+        strncat(package_name, (*token)->text, total_package_name_length - 1);
+        token_toggle_end(*token);
+
+        if (token != &bh_arr_last(package_node->path)) {
+            strncat(package_name, ".", total_package_name_length - 1);
+        }
+    }
+
+    package_node->package_name = package_name;
+    package_node->package = package_lookup(package_name);
+
+    return package_node;
+}
+
+static Package* parse_file_package(OnyxParser* parser) {
+    if (parser->curr->type != Token_Type_Keyword_Package) {
+        return package_lookup_or_create("main", context.global_scope, parser->allocator, parser->curr->pos);
+    }
+
+    AstPackage* package_node = parse_package_expression(parser);
+
+    char aggregate_name[2048];
+    aggregate_name[0] = '\0';
+
+    Package* prevpackage = NULL;
+
+    bh_arr_each(OnyxToken *, symbol, package_node->path) {
+        token_toggle_end(*symbol);
+
+        strncat(aggregate_name, (*symbol)->text, 2047);
+        Package* newpackage = package_lookup_or_create(aggregate_name, context.global_scope, parser->allocator, package_node->token->pos);
+
+        AstPackage* pnode = make_node(AstPackage, Ast_Kind_Package);
+        pnode->token = *symbol;
+        pnode->package = newpackage;
+        pnode->package_name = newpackage->name;
+
+        if (prevpackage != NULL) {
+            symbol_subpackage_introduce(prevpackage->scope, (*symbol)->text, pnode);
+            package_reinsert_use_packages(prevpackage);
+        }
+
+        token_toggle_end(*symbol);
+        strncat(aggregate_name, ".", 2047);
+
+        prevpackage = newpackage;
+    }
+
+    package_node->package = prevpackage;
+
+    return package_node->package;
+}
+
+static void parse_top_level_statements_until(OnyxParser* parser, TokenType tt) {
+    while (parser->curr->type != tt) {
+        if (parser->hit_unexpected_token) break;
+        if (onyx_has_errors()) break;
+        parse_top_level_statement(parser);
+    }
+}
+
+
+// NOTE: This returns a void* so I don't need to cast it everytime I use it
+void* onyx_ast_node_new(bh_allocator alloc, i32 size, AstKind kind) {
+    void* node = bh_alloc(alloc, size);
+
+    memset(node, 0, size);
+    *(AstKind *) node = kind;
+
+    return node;
+}
+
+OnyxParser onyx_parser_create(bh_allocator alloc, OnyxTokenizer *tokenizer) {
+    OnyxParser parser;
+
+    parser.allocator = alloc;
+    parser.tokenizer = tokenizer;
+    parser.curr = tokenizer->tokens;
+    parser.prev = NULL;
+    parser.hit_unexpected_token = 0;
+    parser.current_scope = NULL;
+    parser.alternate_entity_placement_stack = NULL;
+    parser.current_symbol_stack = NULL;
+    parser.current_function_stack = NULL;
+    parser.scope_flags = NULL;
+    parser.stored_tags = NULL;
+    parser.parse_calls = 1;
+    parser.tag_depth = 0;
+
+    parser.polymorph_context = (PolymorphicContext) {
+        .root_node = NULL,
+        .poly_params = NULL,
+    };
+
+    bh_arr_new(global_heap_allocator, parser.alternate_entity_placement_stack, 4);
+    bh_arr_new(global_heap_allocator, parser.current_symbol_stack, 4);
+    bh_arr_new(global_heap_allocator, parser.scope_flags, 4);
+    bh_arr_new(global_heap_allocator, parser.stored_tags, 4);
+
+    return parser;
+}
+
+void onyx_parser_free(OnyxParser* parser) {
+}
+
+void onyx_parse(OnyxParser *parser) {
+    // NOTE: Skip comments at the beginning of the file
+    while (consume_token_if_next(parser, Token_Type_Comment) || consume_token_if_next(parser, Token_Type_Note));
+
+    parser->package = parse_file_package(parser);
+    parser->file_scope = scope_create(parser->allocator, parser->package->private_scope, parser->tokenizer->tokens[0].pos);
+    parser->current_scope = parser->file_scope;
+
+    parse_top_level_statements_until(parser, Token_Type_End_Stream);
+
+    parser->current_scope = parser->current_scope->parent;
+}
diff --git a/compiler/src/polymorph.h b/compiler/src/polymorph.h
new file mode 100644 (file)
index 0000000..0185b8f
--- /dev/null
@@ -0,0 +1,1091 @@
+
+//
+// Polymorphic Procedures
+//
+
+// This flag is used by some of the procedures that try working with polymorphic things,
+// but need to wait until more information is known. Instead of passing a out parameter
+// into each of these procedures, a single global variable is used instead. If the type
+// checker ever gets multi-threaded, this would have to become a threadlocal variable.
+static b32 flag_to_yield = 0;
+
+// This flag is used in the very special case that you are passing a polymorphic procedure
+// to a polymorphic procedure, and you have enough information to instantiate said procedure
+// in order to resolve the type of one of the return values.
+static b32 doing_nested_polymorph_lookup = 0;
+
+// The name is pretty self-descriptive, but this is a node that is returned from things
+// like polymorphic_proc_lookup when it is determined that everything works so far, but
+// the caller must yield in order to finish checking this polymorphic procedure.
+AstTyped node_that_signals_a_yield = { Ast_Kind_Function, 0 };
+AstTyped node_that_signals_failure = { Ast_Kind_Error, 0 };
+
+static void ensure_polyproc_cache_is_created(AstFunction* pp) {
+    if (pp->concrete_funcs == NULL)        sh_new_arena(pp->concrete_funcs);
+    if (pp->active_queries.hashes == NULL) bh_imap_init(&pp->active_queries, global_heap_allocator, 31);
+}
+
+void insert_poly_sln_into_scope(Scope* scope, AstPolySolution *sln) {
+    AstNode *node = NULL;
+
+    switch (sln->kind) {
+        case PSK_Type:
+            node = onyx_ast_node_new(context.ast_alloc, sizeof(AstTypeRawAlias), Ast_Kind_Type_Raw_Alias);
+            ((AstTypeRawAlias *) node)->token = sln->poly_sym->token;
+            ((AstTypeRawAlias *) node)->to = sln->type;
+            ((AstTypeRawAlias *) node)->type = &basic_types[Basic_Kind_Type_Index];
+            ((AstTypeRawAlias *) node)->type_id = sln->type->id;
+            break;
+
+        case PSK_Value:
+            // CLEANUP: Maybe clone this?
+            assert(sln->value->flags & Ast_Flag_Comptime);
+            node = (AstNode *) sln->value;
+            break;
+    }
+
+    symbol_introduce(scope, sln->poly_sym->token, node);
+}
+
+static void insert_poly_slns_into_scope(Scope* scope, bh_arr(AstPolySolution) slns) {
+    bh_arr_each(AstPolySolution, sln, slns) {
+        insert_poly_sln_into_scope(scope, sln);
+    }
+}
+
+// NOTE: This might return a volatile string. Do not store it without copying it.
+static char* build_poly_solution_key(AstPolySolution* sln) {
+    if (sln->kind == PSK_Type) {
+        return (char *) type_get_unique_name(sln->type);
+
+    } else if (sln->kind == PSK_Value) {
+        static char buffer[256];
+
+        fori (i, 0, 256) buffer[i] = 0;
+
+        if (sln->value->kind == Ast_Kind_NumLit) {
+            strncat(buffer, "NUMLIT:", 127);
+            strncat(buffer, bh_bprintf("%l", ((AstNumLit *) sln->value)->value.l), 127);
+
+        } else {
+            // HACK: For now, the value pointer is just used. This means that
+            // sometimes, even through the solution is the same, it won't be
+            // stored the same.
+            bh_snprintf(buffer, 128, "%p", sln->value);
+        }
+
+        return buffer;
+    }
+
+    return NULL;
+}
+
+// NOTE: This returns a volatile string. Do not store it without copying it.
+static char* build_poly_slns_unique_key(bh_arr(AstPolySolution) slns) {
+    static char key_buf[1024];
+    fori (i, 0, 1024) key_buf[i] = 0;
+
+    bh_arr_each(AstPolySolution, sln, slns) {
+        token_toggle_end(sln->poly_sym->token);
+
+        strncat(key_buf, sln->poly_sym->token->text, 1023);
+        strncat(key_buf, "=", 1023);
+        strncat(key_buf, build_poly_solution_key(sln), 1023);
+        strncat(key_buf, ";", 1023);
+
+        token_toggle_end(sln->poly_sym->token);
+    }
+
+    return key_buf;
+}
+
+// NOTE: This function adds a solidified function to the entity heap for it to be processed
+// later. It optionally can start the function header entity at the code generation state if
+// the header has already been processed.
+static b32 add_solidified_function_entities(AstSolidifiedFunction *solidified_func) {
+    solidified_func->func->flags |= Ast_Flag_Function_Used;
+    solidified_func->func->flags |= Ast_Flag_From_Polymorphism;
+
+    Entity func_header_entity = {
+        .state = Entity_State_Resolve_Symbols,
+        .type = Entity_Type_Function_Header,
+        .function = solidified_func->func,
+        .package = NULL,
+        .scope = solidified_func->func->poly_scope,
+    };
+
+    Entity func_entity = {
+        .state = Entity_State_Resolve_Symbols,
+        .type = Entity_Type_Function,
+        .function = solidified_func->func,
+        .package = NULL,
+        .scope = solidified_func->func->poly_scope,
+    };
+
+    Entity* entity_header = entity_heap_insert(&context.entities, func_header_entity);
+    Entity* entity_body   = entity_heap_insert(&context.entities, func_entity);
+
+    solidified_func->func_header_entity  = entity_header;
+    solidified_func->func->entity_header = entity_header;
+    solidified_func->func->entity_body   = entity_body;
+    solidified_func->func->entity        = entity_body;
+    return 1;
+}
+
+// NOTE: This function is responsible for taking all of the information about generating
+// a new polymorphic variant, and producing a solidified function. It optionally can only
+// generate the header of the function, which is useful for cases such as checking if a
+// set of arguments works for a polymorphic overload option.
+static AstSolidifiedFunction generate_solidified_function(
+    AstFunction* pp,
+    bh_arr(AstPolySolution) slns,
+    OnyxToken* tkn,
+    b32 header_only) {
+
+    AstSolidifiedFunction solidified_func;
+    solidified_func.func_header_entity = NULL;
+
+    // NOTE: Use the position of token if one was provided, otherwise just use NULL.
+    OnyxFilePos poly_scope_pos = { 0 };
+    if (tkn) poly_scope_pos = tkn->pos;
+
+    if (header_only) {
+        solidified_func.func = (AstFunction *) clone_function_header(context.ast_alloc, pp);
+        solidified_func.func->flags |= Ast_Flag_Incomplete_Body;
+
+    } else {
+        solidified_func.func = (AstFunction *) ast_clone(context.ast_alloc, pp);
+    }
+
+    solidified_func.func->poly_scope = scope_create(context.ast_alloc, pp->parent_scope_of_poly_proc, poly_scope_pos);
+    insert_poly_slns_into_scope(solidified_func.func->poly_scope, slns);
+
+    solidified_func.func->flags |= Ast_Flag_From_Polymorphism;
+    solidified_func.func->generated_from = tkn;
+
+    // HACK: Remove the baked parameters from the function defintion so they can be
+    // resolved in the poly scope above the function. This does feel kinda of gross
+    // and I would love an alternative to tell it to just "skip" the parameter, but
+    // that is liable to breaking because it is one more thing to remember.
+    //                                             - brendanfh 2021/01/18
+    u32 removed_params = 0;
+    bh_arr_each(AstPolyParam, param, pp->poly_params) {
+        if (param->kind != PPK_Baked_Value) continue;
+
+        bh_arr_deleten(solidified_func.func->params, param->idx - removed_params, 1);
+        removed_params++;
+    }
+
+    return solidified_func;
+}
+
+static void ensure_solidified_function_has_body(AstFunction* pp, AstSolidifiedFunction *solidified_func) {
+    if (solidified_func->func->flags & Ast_Flag_Incomplete_Body) {
+        clone_function_body(context.ast_alloc, solidified_func->func, pp);
+
+        // HACK: I'm asserting that this function should return without an error, because
+        // the only case where it can return an error is if there was a problem with the
+        // header. This should never be the case in this situation, since the header would
+        // have to have successfully passed type checking before it would become a solidified
+        // procedure.
+        assert(add_solidified_function_entities(solidified_func));
+
+        solidified_func->func->flags &= ~Ast_Flag_Incomplete_Body;
+    }
+}
+
+// NOTE: These are temporary data structures used to represent the pattern matching system
+// of polymorphic type resolution.
+typedef struct PolySolveResult {
+    PolySolutionKind kind;
+    union {
+        AstTyped* value;
+        Type*     actual;
+    };
+} PolySolveResult;
+
+typedef struct PolySolveElem {
+    AstType* type_expr;
+
+    PolySolutionKind kind;
+    union {
+        AstTyped* value;
+        Type*     actual;
+    };
+} PolySolveElem;
+
+// NOTE: The job of this function is to solve for the type/value that belongs in a
+// polymorphic variable. This function takes in three arguments:
+//  * The symbol node of the polymorphic parameter being searched for
+//  * The type expression that should contain the symbol node it is some where
+//  * The actual type to pattern match against
+//
+// This function utilizes a basic breadth-first search of the type_expr and actual type
+// trees, always moving along them in parallel, so when the target is reached (if it is
+// ever reached), the "actual" is the matched type/value.
+static PolySolveResult solve_poly_type(AstNode* target, AstType* type_expr, Type* actual) {
+    bh_arr(PolySolveElem) elem_queue = NULL;
+    bh_arr_new(global_heap_allocator, elem_queue, 4);
+
+    PolySolveResult result = { PSK_Undefined, { NULL } };
+
+    bh_arr_push(elem_queue, ((PolySolveElem) {
+        .type_expr = type_expr,
+        .kind = PSK_Type,
+        .actual    = actual
+    }));
+
+    while (!bh_arr_is_empty(elem_queue)) {
+        PolySolveElem elem = elem_queue[0];
+        bh_arr_deleten(elem_queue, 0, 1);
+
+        if (elem.type_expr == (AstType *) target) {
+            result.kind = elem.kind;
+
+            assert(elem.kind != PSK_Undefined);
+            if (result.kind == PSK_Type)  result.actual = elem.actual;
+            if (result.kind == PSK_Value) result.value = elem.value;
+            break;
+        }
+
+        if (elem.kind != PSK_Type) continue;
+
+        switch (elem.type_expr->kind) {
+            case Ast_Kind_Pointer_Type: {
+                if (elem.actual->kind != Type_Kind_Pointer) break;
+
+                bh_arr_push(elem_queue, ((PolySolveElem) {
+                    .type_expr = ((AstPointerType *) elem.type_expr)->elem,
+                    .kind = PSK_Type,
+                    .actual = elem.actual->Pointer.elem,
+                }));
+                break;
+            }
+
+            case Ast_Kind_Address_Of: {
+                if (elem.actual->kind != Type_Kind_Pointer) break;
+
+                bh_arr_push(elem_queue, ((PolySolveElem) {
+                    .type_expr = (AstType *) ((AstAddressOf *) elem.type_expr)->expr,
+                    .kind = PSK_Type,
+                    .actual = elem.actual->Pointer.elem,
+                }));
+                break;
+            }
+
+            case Ast_Kind_Array_Type: {
+                if (elem.actual->kind != Type_Kind_Array) break;
+
+                bh_arr_push(elem_queue, ((PolySolveElem) {
+                    .type_expr = (AstType*) ((AstArrayType *) elem.type_expr)->count_expr,
+                    .kind = PSK_Value,
+
+                    // CLEANUP: Making an integer literal every time is very very very gross. This should
+                    // at least be cached or something.
+                    .value = (AstTyped *) make_int_literal(context.ast_alloc, elem.actual->Array.count)
+                }));
+
+                bh_arr_push(elem_queue, ((PolySolveElem) {
+                    .type_expr = ((AstArrayType *) elem.type_expr)->elem,
+                    .kind = PSK_Type,
+                    .actual = elem.actual->Array.elem,
+                }));
+                break;
+            }
+
+            case Ast_Kind_Slice_Type: {
+                if (elem.actual->kind != Type_Kind_Slice && elem.actual->kind != Type_Kind_DynArray
+                        && elem.actual->kind != Type_Kind_VarArgs && elem.actual->kind != Type_Kind_Array) break;
+
+                bh_arr_push(elem_queue, ((PolySolveElem) {
+                    .type_expr = ((AstSliceType *) elem.type_expr)->elem,
+                    .kind = PSK_Type,
+
+                    // HACK: This makes the assumption that arrays, slices, dynamic arrays and varargs have the same element type at the same location.
+                    .actual = elem.actual->Slice.elem,
+                }));
+                break;
+            }
+
+            case Ast_Kind_DynArr_Type: {
+                if (elem.actual->kind != Type_Kind_DynArray) break;
+
+                bh_arr_push(elem_queue, ((PolySolveElem) {
+                    .type_expr = ((AstDynArrType *) elem.type_expr)->elem,
+                    .kind = PSK_Type,
+                    .actual = elem.actual->DynArray.elem,
+                }));
+                break;
+            }
+
+            case Ast_Kind_VarArg_Type:
+                bh_arr_push(elem_queue, ((PolySolveElem) {
+                    .type_expr = ((AstVarArgType *) elem.type_expr)->elem,
+                    .kind = PSK_Type,
+                    .actual = actual,
+                }));
+                break;
+
+            case Ast_Kind_Function_Type: {
+                if (elem.actual->kind != Type_Kind_Function) break;
+
+                AstFunctionType* ft = (AstFunctionType *) elem.type_expr;
+
+                fori (i, 0, (i64) ft->param_count) {
+                    bh_arr_push(elem_queue, ((PolySolveElem) {
+                        .type_expr = ft->params[i],
+                        .kind = PSK_Type,
+                        .actual = elem.actual->Function.params[i],
+                    }));
+                }
+
+                bh_arr_push(elem_queue, ((PolySolveElem) {
+                    .type_expr = ft->return_type,
+                    .kind = PSK_Type,
+                    .actual = elem.actual->Function.return_type,
+                }));
+
+                break;
+            }
+
+            case Ast_Kind_Call: {
+                AstPolyCallType *pct = convert_call_to_polycall((AstCall *) elem.type_expr);
+                elem.type_expr = (AstType *) pct;
+
+                // fallthrough
+            }
+
+            case Ast_Kind_Poly_Call_Type: {
+                if (elem.actual->kind != Type_Kind_Struct) break;
+                if (bh_arr_length(elem.actual->Struct.poly_sln) != bh_arr_length(((AstPolyCallType *) elem.type_expr)->params)) break;
+
+                AstPolyCallType* pt = (AstPolyCallType *) elem.type_expr;
+
+                fori (i, 0, bh_arr_length(pt->params)) {
+                    PolySolutionKind kind = elem.actual->Struct.poly_sln[i].kind;
+                    if (kind == PSK_Type) {
+                        bh_arr_push(elem_queue, ((PolySolveElem) {
+                            .kind = kind,
+                            .type_expr = (AstType *) pt->params[i],
+                            .actual = elem.actual->Struct.poly_sln[i].type,
+                        }));
+                    } else {
+                        bh_arr_push(elem_queue, ((PolySolveElem) {
+                            .kind = kind,
+                            .type_expr = (AstType *) pt->params[i],
+                            .value = elem.actual->Struct.poly_sln[i].value,
+                        }));
+                    }
+                }
+
+                break;
+            }
+
+            case Ast_Kind_Type_Compound: {
+                if (elem.actual->kind != Type_Kind_Compound) break;
+                if (elem.actual->Compound.count != (u32) bh_arr_length(((AstCompoundType *) elem.type_expr)->types)) break;
+
+                AstCompoundType* ct = (AstCompoundType *) elem.type_expr;
+
+                fori (i, 0, bh_arr_length(ct->types)) {
+                    bh_arr_push(elem_queue, ((PolySolveElem) {
+                        .kind = PSK_Type,
+                        .type_expr = ct->types[i],
+                        .actual = elem.actual->Compound.types[i],
+                    }));
+                }
+
+                break;
+            }
+
+            default: break;
+        }
+    }
+
+    bh_arr_free(elem_queue);
+
+    return result;
+}
+
+// NOTE: The job of this function is to take a polymorphic parameter and a set of arguments
+// and solve for the argument that matches the parameter. This is needed because polymorphic
+// procedure resolution has to happen before the named arguments are placed in their correct
+// positions.
+static AstTyped* lookup_param_in_arguments(AstFunction* func, AstPolyParam* param, Arguments* args, char** err_msg) {
+    bh_arr(AstTyped *) arg_arr = args->values;
+    bh_arr(AstNamedValue *) named_values = args->named_values;
+
+    if ((i32) param->idx < 0)
+        return NULL;
+
+    // NOTE: This check is safe because currently the arguments given without a name
+    // always map to the beginning indidies of the argument array.
+    if (param->idx >= (u64) bh_arr_length(arg_arr)) {
+        OnyxToken* param_name = func->params[param->idx].local->token;
+
+        bh_arr_each(AstNamedValue *, named_value, named_values) {
+            if (token_equals(param_name, (*named_value)->token)) {
+                return (AstTyped *) (*named_value)->value;
+            }
+        }
+
+        if (param->idx <= (u64) bh_arr_length(func->params)) {
+            if (func->params[param->idx].default_value) {
+                return (AstTyped *) func->params[param->idx].default_value;
+            }
+        }
+
+        // CLEANUP
+        if (err_msg) *err_msg = "Not enough arguments to polymorphic procedure. This error message may not be entirely right.";
+
+    } else {
+        return (AstTyped *) arg_arr[param->idx];
+    }
+
+    return NULL;
+}
+
+static AstTyped* try_lookup_based_on_partial_function_type(AstFunction *pp, AstFunctionType *ft) {
+    if (ft->partial_function_type == NULL) {
+        AstType *old_return_type = ft->return_type;
+        ft->return_type = (AstType *) &basic_type_void;
+        ft->partial_function_type = type_build_from_ast(context.ast_alloc, (AstType *) ft);
+        ft->return_type = old_return_type;
+        if (!ft->partial_function_type) {
+            doing_nested_polymorph_lookup = 1;
+            return NULL;
+        }
+
+        assert(ft->partial_function_type);
+    }
+
+    AstTyped *result = (AstTyped *) polymorphic_proc_lookup(pp, PPLM_By_Function_Type, ft->partial_function_type, pp->token);
+    if (result && result->type == NULL) {
+        doing_nested_polymorph_lookup = 1;
+        result = NULL;
+    }
+    if (result == &node_that_signals_a_yield) {
+        doing_nested_polymorph_lookup = 1;
+        result = NULL;
+    }
+
+    return result;
+}
+
+// NOTE: The job of this function is to solve for type of AstPolySolution using the provided
+// information. It is asssumed that the "param" is of kind PPK_Poly_Type. This function uses
+// either the arguments provided, or a function type to compare against to pattern match for
+// the type that the parameter but be.
+static void solve_for_polymorphic_param_type(PolySolveResult* resolved, AstFunction* func, AstPolyParam* param, PolyProcLookupMethod pp_lookup, ptr actual, char** err_msg) {
+    Type* actual_type = NULL;
+
+    switch (pp_lookup) {
+        case PPLM_By_Arguments: {
+            Arguments* args = (Arguments *) actual;
+
+            AstTyped* typed_param = lookup_param_in_arguments(func, param, args, err_msg);
+            if (typed_param == NULL) return;
+
+            // CLEANUP FIXME HACK TODO GROSS
+            if (typed_param->kind == Ast_Kind_Argument) {
+                AstTyped* potential = ((AstArgument *) typed_param)->value;
+                if (potential->kind == Ast_Kind_Polymorphic_Proc) {
+                    if (param->idx < (u32) bh_arr_length(func->params)) {
+                        AstType *param_type = func->params[param->idx].local->type_node;
+                        if (param_type->kind == Ast_Kind_Function_Type) {
+                            AstFunctionType *ft = (AstFunctionType *) param_type;
+                            b32 all_types = 1;
+                            fori (i, 0, (i32) ft->param_count) {
+                                if (!node_is_type((AstNode *) ft->params[i])) {
+                                    all_types = 0;
+                                    break;
+                                }
+                            }
+
+                            if (all_types) {
+                                typed_param = try_lookup_based_on_partial_function_type((AstFunction *) potential, ft);
+                            }
+                        }
+                    }
+                }
+            }
+
+            actual_type = resolve_expression_type(typed_param);
+            if (actual_type == NULL) return;
+
+            break;
+        }
+
+        case PPLM_By_Function_Type: {
+            Type* ft = (Type *) actual;
+            if (param->idx >= ft->Function.param_count) {
+                if (err_msg) *err_msg = "Incompatible polymorphic argument to function parameter.";
+                return;
+            }
+
+            actual_type = ft->Function.params[param->idx];
+            break;
+        }
+
+        default: return;
+    }
+
+    PolySolveResult res = solve_poly_type(param->poly_sym, param->type_expr, actual_type);
+    if (res.kind == PSK_Undefined) {
+        *err_msg = bh_aprintf(global_scratch_allocator,
+            "Unable to solve for polymorphic variable '%b', given the type '%s'.",
+            param->poly_sym->token->text,
+            param->poly_sym->token->length,
+            type_get_name(actual_type));
+    }
+
+    *resolved = res;
+}
+
+
+// NOTE: The job of this function is to look through the arguments provided and find a matching
+// value that is to be baked into the polymorphic procedures poly-scope. It expected that param
+// will be of kind PPK_Baked_Value. In other words, this handles the ($Baked: type) case.
+// CLEANUP: This function is kind of gross at the moment, because it handles different cases for
+// the argument kind. When type expressions (type_expr) become first-class types in the type
+// system, this code should be able to be a lot cleaner.
+static void solve_for_polymorphic_param_value(PolySolveResult* resolved, AstFunction* func, AstPolyParam* param, PolyProcLookupMethod pp_lookup, ptr actual, char** err_msg) {
+    if (pp_lookup != PPLM_By_Arguments) {
+        *err_msg = "Function type cannot be used to solved for baked parameter value.";
+        return;
+    }
+
+    Arguments* args = (Arguments *) actual;
+    AstTyped* value = lookup_param_in_arguments(func, param, args, err_msg);
+    if (value == NULL) return;
+
+    // HACK: Storing the original value because if this was an AstArgument, we need to flag
+    // it as baked if it is determined that the argument is of the correct kind and type.
+    AstTyped* orig_value = value;
+    if (value->kind == Ast_Kind_Argument) {
+        ((AstArgument *) orig_value)->is_baked = 0;
+        value = ((AstArgument *) value)->value;
+    }
+
+    Type*    param_type = NULL;
+    AstType *param_type_expr = func->params[param->idx].local->type_node;
+    if (param_type_expr == (AstType *) &basic_type_type_expr) {
+        if (!node_is_type((AstNode *) value)) {
+            if (err_msg) *err_msg = "Expected type expression.";
+            return;
+        }
+
+        Type* resolved_type = type_build_from_ast(context.ast_alloc, (AstType *) value);
+        if (resolved_type == NULL) flag_to_yield = 1;
+
+        *resolved = ((PolySolveResult) { PSK_Type, .actual = resolved_type });
+
+    } else {
+        resolve_expression_type(value);
+
+        if ((value->flags & Ast_Flag_Comptime) == 0) {
+            if (err_msg) *err_msg = "Expected compile-time known argument.";
+            return;
+        }
+
+        param_type = type_build_from_ast(context.ast_alloc, param_type_expr);
+        if (param_type == NULL) {
+            flag_to_yield = 1;
+            *err_msg = "Waiting to know type for polymorphic value.";
+            return;
+        }
+
+        AstTyped* value_to_use = value;
+        if (value->kind == Ast_Kind_Macro) {
+            value_to_use = (AstTyped *) get_function_from_node((AstNode *) value);
+        }
+
+        TypeMatch tm = unify_node_and_type(&value_to_use, param_type);
+        if (tm == TYPE_MATCH_FAILED) {
+            if (err_msg) *err_msg = bh_aprintf(global_scratch_allocator,
+                    "The procedure '%s' expects a value of type '%s' for baked %d%s parameter, got '%s'.",
+                    get_function_name(func),
+                    type_get_name(param_type),
+                    param->idx + 1,
+                    bh_num_suffix(param->idx + 1),
+                    node_get_type_name(value_to_use));
+            return;
+        }
+
+        if (tm == TYPE_MATCH_YIELD) flag_to_yield = 1;
+
+        *resolved = ((PolySolveResult) { PSK_Value, value });
+    }
+
+    if (orig_value->kind == Ast_Kind_Argument) {
+        ((AstArgument *) orig_value)->is_baked = 1;
+    }
+}
+
+TypeMatch find_polymorphic_sln(AstPolySolution *out, AstPolyParam *param, AstFunction *func, PolyProcLookupMethod pp_lookup, ptr actual, char** err_msg) {
+    // NOTE: Solve for the polymorphic parameter's value
+    PolySolveResult resolved = { PSK_Undefined };
+    switch (param->kind) {
+        case PPK_Poly_Type:   solve_for_polymorphic_param_type (&resolved, func, param, pp_lookup, actual, err_msg); break;
+        case PPK_Baked_Value: solve_for_polymorphic_param_value(&resolved, func, param, pp_lookup, actual, err_msg); break;
+
+        default: if (err_msg) *err_msg = "Invalid polymorphic parameter kind. This is a compiler bug.";
+    }
+
+    if (doing_nested_polymorph_lookup) {
+        doing_nested_polymorph_lookup = 0;
+        return TYPE_MATCH_SPECIAL;
+    }
+
+    if (flag_to_yield) {
+        flag_to_yield = 0;
+        return TYPE_MATCH_YIELD;
+    }
+
+    switch (resolved.kind) {
+        case PSK_Type:
+            out->kind = PSK_Type;
+            out->poly_sym = param->poly_sym;
+            out->type = resolved.actual;
+            return TYPE_MATCH_SUCCESS;
+
+        case PSK_Value:
+            out->kind = PSK_Value;
+            out->poly_sym = param->poly_sym;
+            out->value = resolved.value;
+            return TYPE_MATCH_SUCCESS;
+
+        case PSK_Undefined:
+        default:
+            // NOTE: If no error message has been assigned to why this polymorphic parameter
+            // resolution was unsuccessful, provide a basic dummy one.
+            if (err_msg && *err_msg == NULL)
+                *err_msg = bh_aprintf(global_scratch_allocator,
+                    "Unable to solve for polymorphic variable '%b'.",
+                    param->poly_sym->token->text,
+                    param->poly_sym->token->length);
+
+            out->kind = PSK_Undefined;
+            return TYPE_MATCH_FAILED;
+    }
+}
+
+// NOTE: The job of this function is to take a polymorphic procedure, as well as a method of
+// solving for the polymorphic variables, in order to return an array of the solutions for all
+// of the polymorphic variables.
+static bh_arr(AstPolySolution) find_polymorphic_slns(AstFunction* pp, PolyProcLookupMethod pp_lookup, ptr actual, OnyxToken *tkn, b32 necessary) {
+    ensure_polyproc_cache_is_created(pp);
+    if (bh_imap_has(&pp->active_queries, (u64) actual)) {
+        AstPolyQuery *query = (AstPolyQuery *) bh_imap_get(&pp->active_queries, (u64) actual);
+        assert(query->kind == Ast_Kind_Polymorph_Query);
+        assert(query->entity);
+
+        if (query->entity->state == Entity_State_Finalized) return query->slns;
+        if (query->entity->state == Entity_State_Failed)    return NULL;
+
+        flag_to_yield = 1;
+        return NULL;
+    }
+
+    bh_arr(AstPolySolution) slns = NULL;
+    bh_arr_new(global_heap_allocator, slns, bh_arr_length(pp->poly_params));
+
+    // NOTE: "known solutions" are given through a '#solidify' directive. If this polymorphic
+    // procedure is the result of a partially applied solidification, this array will be non-
+    // empty and these solutions will be used.
+    bh_arr_each(AstPolySolution, known_sln, pp->known_slns) bh_arr_push(slns, *known_sln);
+
+    AstPolyQuery *query = onyx_ast_node_new(context.ast_alloc, sizeof(AstPolyQuery), Ast_Kind_Polymorph_Query);
+    query->token = pp->token;
+    query->proc = pp;
+    query->pp_lookup = pp_lookup;
+    query->given = actual;
+    query->error_loc = tkn;
+    query->slns = slns;
+    query->function_header = clone_function_header(context.ast_alloc, pp);
+    query->function_header->flags |= Ast_Flag_Header_Check_No_Error;
+    query->function_header->scope = NULL;
+    query->error_on_fail = necessary;
+    query->successful_symres = 1;
+
+    bh_imap_put(&pp->active_queries, (u64) actual, (u64) query);
+    add_entities_for_node(NULL, (AstNode *) query, NULL, NULL);
+
+    flag_to_yield = 1;
+    return NULL;
+}
+
+// NOTE: The job of this function is to be a wrapper to other functions, providing an error
+// message if a solution could not be found. This can't be merged with polymorphic_proc_solidify
+// because polymorphic_proc_try_solidify uses the aforementioned function.
+AstFunction* polymorphic_proc_lookup(AstFunction* pp, PolyProcLookupMethod pp_lookup, ptr actual, OnyxToken* tkn) {
+    ensure_polyproc_cache_is_created(pp);
+
+    bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, pp_lookup, actual, tkn, 1);
+    if (slns == NULL) {
+        if (flag_to_yield) {
+            flag_to_yield = 0;
+            return (AstFunction *) &node_that_signals_a_yield;
+        }
+
+        return NULL;
+    }
+
+    AstFunction* result = polymorphic_proc_solidify(pp, slns, tkn);
+    return result;
+}
+
+AstFunction* polymorphic_proc_solidify(AstFunction* pp, bh_arr(AstPolySolution) slns, OnyxToken* tkn) {
+    ensure_polyproc_cache_is_created(pp);
+
+    // NOTE: Check if a version of this polyproc has already been created.
+    char* unique_key = build_poly_slns_unique_key(slns);
+    i32 index = shgeti(pp->concrete_funcs, unique_key);
+    if (index != -1) {
+        AstSolidifiedFunction solidified_func = pp->concrete_funcs[index].value;
+
+        // NOTE: If this solution was originally created from a "build_only_header" call, then the body
+        // will not have been or type checked, or anything. This ensures that the body is copied, the
+        // entities are created and entered into the pipeline.
+        ensure_solidified_function_has_body(pp, &solidified_func);
+
+        // NOTE: Again, if this came from a "build_only_header" call, then there was no known token and
+        // the "generated_from" member will be null. It is best to set it here so errors reported in that
+        // function can report where the polymorphic instantiation occurred.
+        if (solidified_func.func->generated_from == NULL)
+            solidified_func.func->generated_from = tkn;
+
+        return solidified_func.func;
+    }
+
+    AstSolidifiedFunction solidified_func = generate_solidified_function(pp, slns, tkn, 0);
+    add_solidified_function_entities(&solidified_func);
+
+    // NOTE: Cache the function for later use, reducing duplicate functions.
+    shput(pp->concrete_funcs, unique_key, solidified_func);
+
+    return (AstFunction *) &node_that_signals_a_yield;
+}
+
+// NOTE: This can return either a AstFunction or an AstFunction, depending if enough parameters were
+// supplied to remove all the polymorphic variables from the function.
+AstNode* polymorphic_proc_try_solidify(AstFunction* pp, bh_arr(AstPolySolution) slns, OnyxToken* tkn) {
+    i32 valid_argument_count = 0;
+
+    bh_arr_each(AstPolySolution, sln, slns) {
+        b32 found_match = 0;
+
+        bh_arr_each(AstPolyParam, param, pp->poly_params) {
+            if (token_equals(sln->poly_sym->token, param->poly_sym->token)) {
+                found_match = 1;
+                break;
+            }
+        }
+
+        if (found_match) {
+            valid_argument_count++;
+        } else {
+            if (pp->name) {
+                onyx_report_error(tkn->pos, Error_Critical, "'%b' is not a type variable of '%s'.",
+                    sln->poly_sym->token->text, sln->poly_sym->token->length, pp->name);
+            } else {
+                onyx_report_error(tkn->pos, Error_Critical, "'%b' is not a type variable of '%b'.",
+                    sln->poly_sym->token->text, sln->poly_sym->token->length,
+                    pp->token->text, pp->token->length);
+            }
+            return (AstNode *) pp;
+        }
+    }
+
+    if (valid_argument_count == bh_arr_length(pp->poly_params)) {
+        return (AstNode *) polymorphic_proc_solidify(pp, slns, tkn);
+
+    } else {
+        // HACK: Some of these initializations assume that the entity for this polyproc has
+        // made it through the symbol resolution phase.
+        //                                                    - brendanfh 2020/12/25
+        AstFunction* new_pp = onyx_ast_node_new(context.ast_alloc, sizeof(AstFunction), Ast_Kind_Polymorphic_Proc);
+        memcpy(new_pp, pp, sizeof(AstFunction));
+        new_pp->token = tkn;
+        new_pp->poly_params = bh_arr_copy(context.ast_alloc, pp->poly_params);
+
+        ensure_polyproc_cache_is_created(pp);
+        new_pp->concrete_funcs = pp->concrete_funcs;
+
+        new_pp->known_slns = NULL;
+        bh_arr_new(global_heap_allocator, new_pp->known_slns, bh_arr_length(pp->known_slns) + bh_arr_length(slns));
+
+        bh_arr_each(AstPolySolution, sln, pp->known_slns) bh_arr_push(new_pp->known_slns, *sln);
+        bh_arr_each(AstPolySolution, sln, slns)           bh_arr_push(new_pp->known_slns, *sln);
+
+        return (AstNode *) new_pp;
+    }
+}
+
+AstFunction* polymorphic_proc_build_only_header(AstFunction* pp, PolyProcLookupMethod pp_lookup, ptr actual) {
+    ensure_polyproc_cache_is_created(pp);
+    bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, pp_lookup, actual, NULL, 0);
+    if (flag_to_yield) {
+        flag_to_yield = 0;
+        return (AstFunction *) &node_that_signals_a_yield;
+    }
+    if (slns == NULL) return NULL;
+
+    ensure_polyproc_cache_is_created(pp);
+
+    return polymorphic_proc_build_only_header_with_slns(pp, slns, 0);
+}
+
+AstFunction* polymorphic_proc_build_only_header_with_slns(AstFunction* pp, bh_arr(AstPolySolution) slns, b32 error_if_failed) {
+    AstSolidifiedFunction solidified_func;
+
+    char* unique_key = build_poly_slns_unique_key(slns);
+    i32 index = shgeti(pp->concrete_funcs, unique_key);
+    if (index != -1) {
+        solidified_func = pp->concrete_funcs[index].value;
+
+    } else {
+        // NOTE: This function is only going to have the header of it correctly created.
+        // Nothing should happen to this function's body or else the original will be corrupted.
+        //                                                      - brendanfh 2021/01/10
+        solidified_func = generate_solidified_function(pp, slns, NULL, 1);
+    }
+
+    if (solidified_func.func_header_entity) {
+        if (solidified_func.func_header_entity->state == Entity_State_Finalized) return solidified_func.func;
+        if (solidified_func.func_header_entity->state == Entity_State_Failed)    return NULL;
+
+        return (AstFunction *) &node_that_signals_a_yield;
+    }
+
+    BH_MASK_SET(solidified_func.func->flags, !error_if_failed, Ast_Flag_Header_Check_No_Error);
+
+    Entity func_header_entity = {
+        .state = Entity_State_Resolve_Symbols,
+        .type = Entity_Type_Temp_Function_Header,
+        .function = solidified_func.func,
+        .package = NULL,
+        .scope = solidified_func.func->poly_scope,
+    };
+
+    Entity* func_header_entity_ptr = entity_heap_insert(&context.entities, func_header_entity);
+    solidified_func.func_header_entity = func_header_entity_ptr;
+
+    // NOTE: Cache the function for later use.
+    shput(pp->concrete_funcs, unique_key, solidified_func);
+    
+    return (AstFunction *) &node_that_signals_a_yield;
+}
+
+typedef struct AutoPolymorphVariable {
+    u32 idx;
+    u32 variable_count;
+    AstType *base_type;
+    AstType **replace;
+} AutoPolymorphVariable;
+
+// This should be called after all the parameter types have been symresed, but before anything
+// happens to the body.
+b32 potentially_convert_function_to_polyproc(AstFunction *func) {
+    bh_arr(AutoPolymorphVariable) auto_vars = NULL;
+    bh_arr_new(global_heap_allocator, auto_vars, 2);
+
+    u32 param_idx = 0;
+    bh_arr_each(AstParam, param, func->params) {
+        AstType **to_replace = &param->local->type_node;
+        AstType *param_type = param->local->type_node;
+
+        b32 done = 0;
+        while (!done && param_type) {
+            switch (param_type->kind) {
+                case Ast_Kind_Pointer_Type: to_replace = &((AstPointerType *) *to_replace)->elem;  param_type = ((AstPointerType *) param_type)->elem;  break;
+                case Ast_Kind_Array_Type:   to_replace = &((AstArrayType *)   *to_replace)->elem;  param_type = ((AstArrayType *)   param_type)->elem;  break;
+                case Ast_Kind_Slice_Type:   to_replace = &((AstSliceType *)   *to_replace)->elem;  param_type = ((AstSliceType *)   param_type)->elem;  break;
+                case Ast_Kind_DynArr_Type:  to_replace = &((AstDynArrType *)  *to_replace)->elem;  param_type = ((AstDynArrType *)  param_type)->elem;  break;
+                case Ast_Kind_Alias:                                                               param_type = (AstType *) ((AstAlias *) param_type)->alias; break;
+                case Ast_Kind_Type_Alias:                                                          param_type = ((AstTypeAlias *)   param_type)->to;    break;
+                case Ast_Kind_Poly_Struct_Type: {
+                    AutoPolymorphVariable apv;
+                    apv.idx = param_idx;
+                    apv.base_type = param->local->type_node;
+                    apv.variable_count = bh_arr_length(((AstPolyStructType *) param_type)->poly_params);
+                    apv.replace = to_replace;
+
+                    bh_arr_push(auto_vars, apv);
+                    done = 1;
+                    break;
+                }
+
+                default: done = 1; break;
+            }
+        }
+
+        param_idx++;
+    }
+
+    if (bh_arr_length(auto_vars) == 0) return 0;
+
+    param_idx = 0;
+    bh_arr_each(AutoPolymorphVariable, apv, auto_vars) {
+        AstPolyParam pp;
+        pp.idx = apv->idx;
+        pp.kind = PPK_Poly_Type;
+
+        AstPolyCallType* pcall = onyx_ast_node_new(context.ast_alloc, sizeof(AstPolyCallType), Ast_Kind_Poly_Call_Type);
+        pcall->callee = *apv->replace;
+        pcall->token = pcall->callee->token;
+        bh_arr_new(global_heap_allocator, pcall->params, apv->variable_count);
+
+        if (apv->base_type->kind == Ast_Kind_Poly_Struct_Type) {
+            pp.type_expr = (AstType *) pcall;
+        } else {
+            pp.type_expr = apv->base_type;
+        }
+        *apv->replace = (AstType *) pcall;
+
+        fori (i, 0, apv->variable_count) {
+            OnyxToken* name_token = bh_alloc_item(context.ast_alloc, OnyxToken);
+            name_token->text = bh_aprintf(context.ast_alloc, "__autopoly_var_%d\0", param_idx);
+            name_token->length = strlen(name_token->text);
+            name_token->type = Token_Type_Symbol;
+            name_token->pos  = pcall->token->pos;
+
+            pp.poly_sym = make_symbol(context.ast_alloc, name_token);
+            bh_arr_push(pcall->params, pp.poly_sym);
+            bh_arr_push(func->poly_params, pp);
+            param_idx ++;
+        }
+    }
+
+    convert_function_to_polyproc(func);
+
+    bh_arr_each(AstParam, param, func->params) {
+        param->local->flags |= Ast_Flag_Param_Symbol_Dirty;
+    }
+
+    return 1;
+}
+
+//
+// Polymorphic Structures
+//
+//
+// Currently, I am not very happy about how polymorphic structure generation works. My biggest problem
+// with it is that it is very different from the polymorhic procedure generation. Also, it needs to
+// completely generate and check the structure right away, which means there is a lot of up-front work
+// done here that could probably be done elsewhere. This really relates to a large problem in the compiler
+// that types need to be known completely by the time symbol resolution is done, even though that
+// information shouldn't need to be known until right before the types are checked.
+//
+// The above documentation is very incorrect but I don't want to fix it right now. Basically, polymorphic
+// structures now have a delay instantiation phase and are not forced to be completed immediately.
+
+char* build_poly_struct_name(AstPolyStructType* ps_type, Type* cs_type) {
+    char name_buf[256];
+    fori (i, 0, 256) name_buf[i] = 0;
+
+    strncat(name_buf, ps_type->name, 255);
+    strncat(name_buf, "(", 255);
+    bh_arr_each(AstPolySolution, ptype, cs_type->Struct.poly_sln) {
+        if (ptype != cs_type->Struct.poly_sln)
+            strncat(name_buf, ", ", 255);
+
+        // This logic will have to be other places as well.
+
+        switch (ptype->kind) {
+            case PSK_Undefined: assert(0); break;
+            case PSK_Type:      strncat(name_buf, type_get_name(ptype->type), 255); break;
+            case PSK_Value: {
+                // FIX
+                AstNode* value = strip_aliases((AstNode *) ptype->value);
+
+                if (value->kind == Ast_Kind_NumLit) {
+                    AstNumLit* nl = (AstNumLit *) value;
+                    if (type_is_integer(nl->type)) {
+                        strncat(name_buf, bh_bprintf("%l", nl->value.l), 127);
+                    } else {
+                        strncat(name_buf, "numlit (FIX ME)", 127);
+                    }
+                } else if (value->kind == Ast_Kind_Code_Block) {
+                    AstCodeBlock* code = (AstCodeBlock *) value;
+                    OnyxFilePos code_loc = code->token->pos;
+                    strncat(name_buf, bh_bprintf("code at %s:%d,%d", code_loc.filename, code_loc.line, code_loc.column), 127);
+                } else {
+                    strncat(name_buf, "<expr>", 127);
+                }
+
+                break;
+            }
+        }
+    }
+    strncat(name_buf, ")", 255);
+
+    return bh_aprintf(global_heap_allocator, "%s", name_buf);
+}
+
+Type* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(AstPolySolution) slns, OnyxFilePos pos, b32 error_if_failed) {
+    // @Cleanup
+    assert(ps_type->scope != NULL);
+
+    if (ps_type->concrete_structs == NULL) {
+        sh_new_arena(ps_type->concrete_structs);
+    }
+
+    if (bh_arr_length(slns) != bh_arr_length(ps_type->poly_params)) {
+        onyx_report_error(pos, Error_Critical, "Wrong number of arguments for '%s'. Expected %d, got %d.",
+            ps_type->name,
+            bh_arr_length(ps_type->poly_params),
+            bh_arr_length(slns));
+
+        return NULL;
+    }
+
+    i32 i = 0;
+    bh_arr_each(AstPolySolution, sln, slns) {
+        sln->poly_sym = (AstNode *) &ps_type->poly_params[i];
+        i++;
+    }
+
+    char* unique_key = build_poly_slns_unique_key(slns);
+    i32 index = shgeti(ps_type->concrete_structs, unique_key);
+    if (index != -1) {
+        AstStructType* concrete_struct = ps_type->concrete_structs[index].value;
+
+        if (concrete_struct->entity_type->state < Entity_State_Check_Types) {
+            return NULL;
+        }
+
+        if (concrete_struct->entity_type->state == Entity_State_Failed) {
+            return (Type *) &node_that_signals_failure;
+        }
+
+        Type* cs_type = type_build_from_ast(context.ast_alloc, (AstType *) concrete_struct);
+        if (!cs_type) return NULL;
+
+        if (cs_type->Struct.poly_sln == NULL) cs_type->Struct.poly_sln = bh_arr_copy(global_heap_allocator, slns);
+        if (cs_type->Struct.name == NULL)     cs_type->Struct.name = build_poly_struct_name(ps_type, cs_type);
+
+        return cs_type;
+    }
+
+    Scope* sln_scope = scope_create(context.ast_alloc, ps_type->scope, ps_type->token->pos);
+    insert_poly_slns_into_scope(sln_scope, slns);
+
+    AstStructType* concrete_struct = (AstStructType *) ast_clone(context.ast_alloc, ps_type->base_struct);
+    concrete_struct->polymorphic_error_loc = pos;
+    BH_MASK_SET(concrete_struct->flags, !error_if_failed, Ast_Flag_Header_Check_No_Error);
+
+
+    i64 arg_count = bh_arr_length(ps_type->poly_params);
+    bh_arr_new(global_heap_allocator, concrete_struct->polymorphic_argument_types, arg_count);
+    bh_arr_set_length(concrete_struct->polymorphic_argument_types, arg_count);
+    concrete_struct->polymorphic_arguments = bh_arr_copy(global_heap_allocator, slns);
+
+    fori (i, 0, (i64) bh_arr_length(ps_type->poly_params)) {
+        concrete_struct->polymorphic_argument_types[i] = (AstType *) ast_clone(context.ast_alloc, ps_type->poly_params[i].type_node);
+    }
+
+    shput(ps_type->concrete_structs, unique_key, concrete_struct);
+    add_entities_for_node(NULL, (AstNode *) concrete_struct, sln_scope, NULL);
+    return NULL;
+}
diff --git a/compiler/src/symres.c b/compiler/src/symres.c
new file mode 100644 (file)
index 0000000..4410bb6
--- /dev/null
@@ -0,0 +1,1727 @@
+#define BH_DEBUG
+#include "parser.h"
+#include "utils.h"
+#include "astnodes.h"
+#include "errors.h"
+
+// :EliminatingSymres - notes the places where too much work is being done in symbol resolution
+
+// Variables used during the symbol resolution phase.
+static Scope*       curr_scope    = NULL;
+static b32 report_unresolved_symbols = 1;
+static b32 resolved_a_symbol         = 0;
+
+// Everything related to waiting on is imcomplete at the moment.
+static Entity* waiting_on         = NULL;
+
+#define SYMRES(kind, ...) do { \
+    SymresStatus ss = symres_ ## kind (__VA_ARGS__); \
+    if (ss > Symres_Errors_Start) return ss;         \
+    } while (0)
+
+#define SYMRES_INVISIBLE(kind, node, ...) do { \
+    (node)->flags |= Ast_Flag_Symbol_Invisible; \
+    SymresStatus ss = symres_ ## kind (__VA_ARGS__); \
+    (node)->flags &= ~Ast_Flag_Symbol_Invisible; \
+    if (ss > Symres_Errors_Start) return ss;         \
+    } while (0)
+
+typedef enum SymresStatus {
+    Symres_Success,
+    Symres_Complete,
+    Symres_Goto_Parse,
+
+    Symres_Errors_Start,
+    Symres_Yield_Macro,
+    Symres_Yield_Micro,
+    Symres_Error,
+} SymresStatus;
+
+static SymresStatus symres_type(AstType** type);
+static SymresStatus symres_local(AstLocal** local);
+static SymresStatus symres_call(AstCall** pcall);
+static SymresStatus symres_size_of(AstSizeOf* so);
+static SymresStatus symres_align_of(AstAlignOf* so);
+static SymresStatus symres_field_access(AstFieldAccess** fa);
+static SymresStatus symres_compound(AstCompound* compound);
+static SymresStatus symres_expression(AstTyped** expr);
+static SymresStatus symres_return(AstReturn* ret);
+static SymresStatus symres_if(AstIfWhile* ifnode);
+static SymresStatus symres_while(AstIfWhile* whilenode);
+static SymresStatus symres_for(AstFor* fornode);
+static SymresStatus symres_case(AstSwitchCase *casenode);
+static SymresStatus symres_switch(AstSwitch* switchnode);
+static SymresStatus symres_use(AstUse* use);
+static SymresStatus symres_directive_solidify(AstDirectiveSolidify** psolid);
+static SymresStatus symres_directive_defined(AstDirectiveDefined** pdefined);
+static SymresStatus symres_directive_insert(AstDirectiveInsert* insert);
+static SymresStatus symres_statement_chain(AstNode** walker);
+static SymresStatus symres_statement(AstNode** stmt, b32 *remove);
+static SymresStatus symres_block(AstBlock* block);
+static SymresStatus symres_function_header(AstFunction* func);
+static SymresStatus symres_function(AstFunction* func);
+static SymresStatus symres_global(AstGlobal* global);
+static SymresStatus symres_overloaded_function(AstOverloadedFunction* ofunc);
+static SymresStatus symres_package(AstPackage* package);
+static SymresStatus symres_enum(AstEnumType* enum_node);
+static SymresStatus symres_memres_type(AstMemRes** memres);
+static SymresStatus symres_memres(AstMemRes** memres);
+static SymresStatus symres_struct_defaults(AstType* st);
+static SymresStatus symres_static_if(AstIf* static_if);
+static SymresStatus symres_macro(AstMacro* macro);
+static SymresStatus symres_constraint(AstConstraint* constraint);
+static SymresStatus symres_polyquery(AstPolyQuery *query);
+
+static void scope_enter(Scope* new_scope) {
+    curr_scope = new_scope;
+}
+
+static void scope_leave() {
+    curr_scope = curr_scope->parent;
+}
+
+static SymresStatus symres_symbol(AstNode** symbol_node) {
+    OnyxToken* token = (*symbol_node)->token;
+    AstNode* res = symbol_resolve(curr_scope, token);
+
+    if (!res) { // :SymresStall
+        if (report_unresolved_symbols) {
+            token_toggle_end(token);
+            char *closest = find_closest_symbol_in_scope_and_parents(curr_scope, token->text);
+            token_toggle_end(token);
+
+            if (closest) onyx_report_error(token->pos, Error_Critical, "Unable to resolve symbol '%b'. Did you mean '%s'?", token->text, token->length, closest);
+            else         onyx_report_error(token->pos, Error_Critical, "Unable to resolve symbol '%b'.", token->text, token->length);
+
+            return Symres_Error;
+        } else {
+            return Symres_Yield_Macro;
+        }
+
+    } else {
+        *symbol_node = res;
+        resolved_a_symbol = 1;
+    }
+
+    return Symres_Success;
+}
+
+static SymresStatus symres_struct_type(AstStructType* s_node) {
+    if (s_node->flags & Ast_Flag_Type_Is_Resolved) return Symres_Success;
+
+    s_node->flags |= Ast_Flag_Type_Is_Resolved;
+    s_node->flags |= Ast_Flag_Comptime;
+
+    if (s_node->scope) {
+        // FIX: This is probably wrong for the long term.
+        s_node->scope->parent = curr_scope;
+
+        scope_enter(s_node->scope);
+    }
+    
+    if (s_node->min_size_)      SYMRES(expression, &s_node->min_size_);
+    if (s_node->min_alignment_) SYMRES(expression, &s_node->min_alignment_);
+
+    if (s_node->polymorphic_argument_types) {
+        assert(s_node->polymorphic_arguments);
+
+        SymresStatus ss = Symres_Success, result;
+        fori (i, 0, (i64) bh_arr_length(s_node->polymorphic_argument_types)) {
+            result = symres_type(&s_node->polymorphic_argument_types[i]);
+            if (result > ss) ss = result;
+
+            if (s_node->polymorphic_arguments[i].value) {
+                result = symres_expression(&s_node->polymorphic_arguments[i].value);
+                if (result > ss) ss = result;
+            }
+        }
+    }
+
+    if (s_node->constraints.constraints) {
+        bh_arr_each(AstConstraint *, constraint, s_node->constraints.constraints) {
+            SYMRES(constraint, *constraint);
+        }
+    }
+
+    fori (i, 0, bh_arr_length(s_node->members)) {
+        AstStructMember *member = s_node->members[i];
+
+        if (member->type_node) {
+            SymresStatus ss = symres_type(&member->type_node);
+            if (ss != Symres_Success) {
+                s_node->flags &= ~Ast_Flag_Type_Is_Resolved;
+                if (s_node->scope) scope_leave();
+                return ss;
+            }
+        }
+    }
+
+    if (s_node->scope) scope_leave();
+    return Symres_Success;
+}
+
+static SymresStatus symres_type(AstType** type) {
+    // Don't make this kill all symbol resolution if the type is null.
+    if (!type || !*type) return Symres_Success;
+
+    switch ((*type)->kind) {
+        case Ast_Kind_Symbol:     SYMRES(symbol, (AstNode **) type); break;
+        case Ast_Kind_Basic_Type: break;
+        case Ast_Kind_Type_Alias: SYMRES(type, &((AstTypeAlias *) *type)->to); break;
+        case Ast_Kind_Field_Access: {
+            SYMRES(field_access, (AstFieldAccess **) type);
+            break;
+        }
+
+        case Ast_Kind_Pointer_Type: SYMRES(type, &((AstPointerType *) *type)->elem); break;
+        case Ast_Kind_Slice_Type:   SYMRES(type, &((AstSliceType *) *type)->elem); break;
+        case Ast_Kind_DynArr_Type:  SYMRES(type, &((AstDynArrType *) *type)->elem); break;
+        case Ast_Kind_VarArg_Type:  SYMRES(type, &((AstVarArgType *) *type)->elem); break;
+
+        case Ast_Kind_Function_Type: {
+            AstFunctionType* ftype = (AstFunctionType *) *type;
+
+            if (ftype->param_count > 0) {
+                fori (i, 0, (i64) ftype->param_count) {
+                    SYMRES(type, &ftype->params[i]);
+                }
+            }
+
+            SYMRES(type, &ftype->return_type);
+            break;
+        }
+
+        case Ast_Kind_Struct_Type: SYMRES(struct_type, (AstStructType *) *type); break;
+        case Ast_Kind_Array_Type: {
+            AstArrayType* a_node = (AstArrayType *) *type;
+
+            if (a_node->count_expr) SYMRES(expression, &a_node->count_expr);
+            SYMRES(type, &a_node->elem);
+            break;
+        }
+
+        case Ast_Kind_Enum_Type: break;
+
+        case Ast_Kind_Poly_Struct_Type: {
+            AstPolyStructType* pst_node = (AstPolyStructType *) *type;
+
+            if (pst_node->scope == NULL) {
+                pst_node->scope = scope_create(context.ast_alloc, pst_node->entity->scope, pst_node->token->pos);
+            }
+            break;
+        }
+
+        case Ast_Kind_Poly_Call_Type: {
+            AstPolyCallType* pc_node = (AstPolyCallType *) *type;
+
+            SYMRES(type, &pc_node->callee);
+
+            bh_arr_each(AstNode *, param, pc_node->params) {
+                if (node_is_type(*param)) {
+                    SYMRES(type, (AstType **) param);
+                } else {
+                    SYMRES(expression, (AstTyped **) param);
+                }
+            }
+            break;
+        }
+
+        case Ast_Kind_Type_Compound: {
+            AstCompoundType* ctype = (AstCompoundType *) *type;
+
+            bh_arr_each(AstType *, type, ctype->types) SYMRES(type, type);
+            break;
+        }
+
+        case Ast_Kind_Alias: {
+            AstAlias* alias = (AstAlias *) *type;
+            SYMRES_INVISIBLE(type, alias, (AstType **) &alias->alias);
+
+            break;
+        }
+
+        case Ast_Kind_Typeof: {
+            AstTypeOf* type_of = (AstTypeOf *) *type;
+            SYMRES(expression, &type_of->expr);
+            break;
+        }
+
+        case Ast_Kind_Distinct_Type: {
+            AstDistinctType *distinct = (AstDistinctType *) *type;
+            SYMRES(type, &distinct->base_type);
+            break;
+        }
+    }
+
+    return Symres_Success;
+}
+
+static SymresStatus symres_local(AstLocal** local) {
+    SYMRES(type, &(*local)->type_node);
+
+    if ((*local)->token != NULL)
+        symbol_introduce(curr_scope, (*local)->token, (AstNode *) *local);
+
+    return Symres_Success;
+}
+
+static SymresStatus symres_arguments(Arguments* args) {
+    bh_arr_each(AstTyped *, arg, args->values)
+        SYMRES(expression, arg);
+
+    bh_arr_each(AstNamedValue *, named_arg, args->named_values)
+        SYMRES(expression, &(*named_arg)->value);
+
+    return Symres_Success;
+}
+
+static SymresStatus symres_call(AstCall** pcall) {
+    AstCall *call = *pcall;
+    SYMRES(expression, (AstTyped **) &call->callee);
+    SYMRES(arguments, &call->args);
+
+    AstNode* callee = strip_aliases((AstNode *) call->callee);
+    if (callee->kind == Ast_Kind_Poly_Struct_Type) {
+        *pcall = (AstCall *) convert_call_to_polycall(call);
+        SYMRES(type, (AstType **) pcall);
+        return Symres_Success;
+    }
+
+    return Symres_Success;
+}
+
+static SymresStatus symres_size_of(AstSizeOf* so) {
+    SYMRES(type, &so->type_node);
+    SYMRES(type, &so->so_ast_type);
+
+    return Symres_Success;
+}
+
+static SymresStatus symres_align_of(AstAlignOf* ao) {
+    SYMRES(type, &ao->type_node);
+    SYMRES(type, &ao->ao_ast_type);
+
+    return Symres_Success;
+}
+
+static SymresStatus symres_field_access(AstFieldAccess** fa) {
+    if ((*fa)->expr == NULL) return Symres_Error;
+    SYMRES(expression, &(*fa)->expr);
+    if ((*fa)->expr == NULL) return Symres_Error;
+
+    AstTyped* expr = (AstTyped *) strip_aliases((AstNode *) (*fa)->expr);
+
+    b32 force_a_lookup = 0;
+
+    if (expr->kind == Ast_Kind_Enum_Type || expr->kind == Ast_Kind_Type_Raw_Alias) {
+        force_a_lookup = 1;
+    }
+
+    //
+    // If we are trying to access a field on an alias, we have to make sure
+    // the alias is "ready" to have a symbol looked up inside of it. This means
+    // the alias should have passed symbol resolution. If not, force a lookup
+    // and yield if the alias was not ready.
+    if ((*fa)->expr->kind == Ast_Kind_Alias) {
+        assert((*fa)->expr->entity);
+        if ((*fa)->expr->entity->state < Entity_State_Check_Types) {
+            force_a_lookup = 1;
+        }
+    }
+
+    AstNode* resolution = try_symbol_resolve_from_node((AstNode *) expr, (*fa)->token);
+    if (resolution) *((AstNode **) fa) = resolution;
+    else if (expr->kind == Ast_Kind_Package) {
+        if (report_unresolved_symbols) {
+            token_toggle_end((*fa)->token);
+            char *closest = find_closest_symbol_in_node((AstNode *) expr, (*fa)->token->text);
+            token_toggle_end((*fa)->token);
+
+            AstPackage *package = (AstPackage *) strip_aliases((AstNode *) (*fa)->expr);
+
+            if (closest) {
+                onyx_report_error((*fa)->token->pos, Error_Critical, "'%b' was not found in package '%s'. Did you mean '%s'?",
+                    (*fa)->token->text,
+                    (*fa)->token->length,
+                    package->package->name,
+                    closest);
+            } else {
+                onyx_report_error((*fa)->token->pos, Error_Critical, "'%b' was not found in package '%s'. Perhaps it is defined in a file that wasn't loaded?",
+                    (*fa)->token->text,
+                    (*fa)->token->length,
+                    package->package->name);
+            }
+            return Symres_Error;
+
+        } else {
+            return Symres_Yield_Macro;
+        }
+    }
+    else if (force_a_lookup) {
+        if (context.cycle_detected) {
+            onyx_report_error((*fa)->token->pos, Error_Critical, "'%b' does not exist here. This is a bad error message.",
+                (*fa)->token->text,
+                (*fa)->token->length);
+            return Symres_Error;
+        }
+
+        return Symres_Yield_Macro;
+    }
+
+    return Symres_Success;
+}
+
+static SymresStatus symres_compound(AstCompound* compound) {
+    bh_arr_each(AstTyped *, expr, compound->exprs) {
+        SYMRES(expression, expr);
+    }
+
+    return Symres_Success;
+}
+
+static SymresStatus symres_if_expression(AstIfExpression* if_expr) {
+    SYMRES(expression, &if_expr->cond);
+    SYMRES(expression, &if_expr->true_expr);
+    SYMRES(expression, &if_expr->false_expr);
+    return Symres_Success;
+}
+
+static SymresStatus symres_pipe(AstBinaryOp** pipe) {
+    AstCall* call_node = (AstCall *) (*pipe)->right;
+    SYMRES(expression, (AstTyped **) &call_node);
+    SYMRES(expression, &(*pipe)->left);
+
+    if (call_node->kind != Ast_Kind_Call) {
+        onyx_report_error((*pipe)->token->pos, Error_Critical, "Pipe operator expected call on right side.");
+        return Symres_Error;
+    }
+
+    if ((*pipe)->left == NULL) return Symres_Error;
+
+    // :EliminatingSymres
+    bh_arr_insertn(call_node->args.values, 0, 1);
+    call_node->args.values[0] = (AstTyped *) make_argument(context.ast_alloc, (*pipe)->left);
+    call_node->next = (*pipe)->next;
+
+    // NOTE: Not a BinaryOp node
+    *pipe = (AstBinaryOp *) call_node;
+
+    return Symres_Success;
+}
+
+// CLEANUP: This is an experimental feature and might be removed in the future.
+// I noticed a common pattern when writing in Onyx is something that looks like this:
+//
+//     foo.member_function(^foo, ...)
+//
+// I decided it would be worth adding a bit of syntactic sugar for such as call. I
+// decided to use the '->' operator for this purpose. The snippet below is the exact
+// same as the snippet above (after the nodes have been processed by the function below)
+//
+//     foo->member_function(...)
+static SymresStatus symres_method_call(AstBinaryOp** mcall) {
+    SYMRES(expression, &(*mcall)->left);
+    if ((*mcall)->left == NULL) return Symres_Error;
+
+    // :EliminatingSymres
+    if (((*mcall)->flags & Ast_Flag_Has_Been_Symres) == 0) {
+        if ((*mcall)->right->kind != Ast_Kind_Call) {
+            onyx_report_error((*mcall)->token->pos, Error_Critical, "'->' expected procedure call on right side.");
+            return Symres_Error;
+        }
+
+        AstFieldAccess* implicit_field_access = make_field_access(context.ast_alloc, (*mcall)->left, NULL);
+        implicit_field_access->token = ((AstCall *) (*mcall)->right)->callee->token;
+        ((AstCall *) (*mcall)->right)->callee = (AstTyped *) implicit_field_access;
+        (*mcall)->flags |= Ast_Flag_Has_Been_Symres;
+    }
+
+    assert((*mcall)->right->kind == Ast_Kind_Call);
+    SYMRES(expression, (AstTyped **) &(*mcall)->right);
+
+    return Symres_Success;
+}
+
+static SymresStatus symres_unaryop(AstUnaryOp** unaryop) {
+    if ((*unaryop)->operation == Unary_Op_Cast) {
+        SYMRES(type, &(*unaryop)->type_node);
+    }
+
+    SYMRES(expression, &(*unaryop)->expr);
+
+    return Symres_Success;
+}
+
+static SymresStatus symres_struct_literal(AstStructLiteral* sl) {
+    if (sl->stnode != NULL) SYMRES(expression, &sl->stnode);
+
+    // :EliminatingSymres
+    sl->type_node = (AstType *) sl->stnode;
+    while (sl->type_node && sl->type_node->kind == Ast_Kind_Type_Alias)
+        sl->type_node = ((AstTypeAlias *) sl->type_node)->to;
+
+    SYMRES(arguments, &sl->args);
+
+    return Symres_Success;
+}
+
+static SymresStatus symres_array_literal(AstArrayLiteral* al) {
+    if (al->atnode != NULL) SYMRES(expression, &al->atnode);
+
+    // :EliminatingSymres
+    al->type_node = (AstType *) al->atnode;
+    while (al->type_node && al->type_node->kind == Ast_Kind_Type_Alias)
+        al->type_node = ((AstTypeAlias *) al->type_node)->to;
+
+    bh_arr_each(AstTyped *, expr, al->values)
+        SYMRES(expression, expr);
+
+    return Symres_Success;
+}
+
+static SymresStatus symres_address_of(AstAddressOf** paof) {
+    AstAddressOf *aof = (AstAddressOf *) *paof;
+    SYMRES(expression, &aof->expr);
+
+    AstTyped *expr = (AstTyped *) strip_aliases((AstNode *) aof->expr);
+    if (node_is_type((AstNode *) expr)) {
+        AstPointerType *pt = onyx_ast_node_new(context.ast_alloc, sizeof(AstPointerType), Ast_Kind_Pointer_Type);
+        pt->token     = aof->token;
+        pt->elem      = (AstType *) expr;
+        pt->__unused  = aof->next;
+        *paof         = (AstAddressOf *) pt;
+        SYMRES(type, (AstType **) &pt);
+        return Symres_Success;
+    }
+
+    return Symres_Success;
+}
+
+static SymresStatus symres_expression(AstTyped** expr) {
+    if (node_is_type((AstNode *) *expr)) {
+        SYMRES(type, (AstType **) expr);
+        return Symres_Success;
+    }
+
+    switch ((*expr)->kind) {
+        case Ast_Kind_Symbol: SYMRES(symbol, (AstNode **) expr); break;
+
+        case Ast_Kind_Binary_Op:
+            SYMRES(expression, &((AstBinaryOp *)(*expr))->left);
+            SYMRES(expression, &((AstBinaryOp *)(*expr))->right);
+            break;
+
+        case Ast_Kind_Unary_Op:     SYMRES(unaryop, (AstUnaryOp **) expr); break;
+        case Ast_Kind_Call:         SYMRES(call, (AstCall **) expr); break;
+        case Ast_Kind_Argument:     SYMRES(expression, &((AstArgument *) *expr)->value); break;
+        case Ast_Kind_Block:        SYMRES(block, (AstBlock *) *expr); break;
+        case Ast_Kind_Dereference:  SYMRES(expression, &((AstDereference *)(*expr))->expr); break;
+        case Ast_Kind_Field_Access: SYMRES(field_access, (AstFieldAccess **) expr); break;
+        case Ast_Kind_Pipe:         SYMRES(pipe, (AstBinaryOp **) expr); break;
+        case Ast_Kind_Method_Call:  SYMRES(method_call, (AstBinaryOp **) expr); break;
+        case Ast_Kind_Size_Of:      SYMRES(size_of, (AstSizeOf *)*expr); break;
+        case Ast_Kind_Align_Of:     SYMRES(align_of, (AstAlignOf *)*expr); break;
+        case Ast_Kind_Address_Of:   SYMRES(address_of, (AstAddressOf **) expr); break;
+        case Ast_Kind_Alias: {
+            AstAlias *alias = (AstAlias *) *expr;
+            SYMRES_INVISIBLE(expression, alias, &alias->alias);
+            break;
+        }
+
+        case Ast_Kind_Range_Literal:
+            SYMRES(expression, &((AstRangeLiteral *)(*expr))->low);
+            SYMRES(expression, &((AstRangeLiteral *)(*expr))->high);
+
+            // :EliminatingSymres
+            SYMRES(type, &builtin_range_type);
+            (*expr)->type_node = builtin_range_type;
+            break;
+
+        case Ast_Kind_Function:
+        case Ast_Kind_NumLit:
+            SYMRES(type, &(*expr)->type_node);
+            break;
+
+        case Ast_Kind_StrLit: {
+            AstStrLit* str = (AstStrLit *) *expr;
+            if (str->is_cstr) {
+                SYMRES(type, &builtin_cstring_type);
+                str->type_node = builtin_cstring_type;
+
+            } else {
+                SYMRES(type, &builtin_string_type);
+                str->type_node = builtin_string_type;
+            }
+            break;
+        }
+
+        case Ast_Kind_Slice:
+        case Ast_Kind_Subscript:
+            SYMRES(expression, &((AstSubscript *)(*expr))->addr);
+            SYMRES(expression, &((AstSubscript *)(*expr))->expr);
+            break;
+
+        case Ast_Kind_Struct_Literal:
+            SYMRES(struct_literal, (AstStructLiteral *)(*expr));
+            break;
+
+        case Ast_Kind_Array_Literal:
+            SYMRES(array_literal, (AstArrayLiteral *)(*expr));
+            break;
+
+        case Ast_Kind_Directive_Solidify:
+            SYMRES(directive_solidify, (AstDirectiveSolidify **) expr);
+            break;
+
+        case Ast_Kind_Directive_Defined:
+            SYMRES(directive_defined, (AstDirectiveDefined **) expr);
+            break;
+
+        case Ast_Kind_Compound:
+            SYMRES(compound, (AstCompound *) *expr);
+            break;
+
+        case Ast_Kind_Package:
+            SYMRES(package, (AstPackage *) *expr);
+            break;
+
+        case Ast_Kind_If_Expression:
+            SYMRES(if_expression, (AstIfExpression *) *expr);
+            break;
+
+        case Ast_Kind_Directive_Insert:
+            SYMRES(directive_insert, (AstDirectiveInsert *) *expr);
+            break;
+
+        case Ast_Kind_Do_Block:
+            SYMRES(block, ((AstDoBlock *) *expr)->block);
+            break;
+
+        case Ast_Kind_Param:
+            if ((*expr)->flags & Ast_Flag_Param_Symbol_Dirty) {
+                assert((*expr)->token->type == Token_Type_Symbol);
+                *expr = (AstTyped *) make_symbol(context.ast_alloc, (*expr)->token);
+                SYMRES(expression, expr);
+            }
+            break;
+
+        default: break;
+    }
+
+    return Symres_Success;
+}
+
+static SymresStatus symres_return(AstReturn* ret) {
+    if (ret->expr)
+        SYMRES(expression, &ret->expr);
+
+    return Symres_Success;
+}
+
+static SymresStatus symres_if(AstIfWhile* ifnode) {
+    if (ifnode->kind == Ast_Kind_Static_If) {
+        if ((ifnode->flags & Ast_Flag_Static_If_Resolved) == 0) {
+            if (context.cycle_detected) {
+                onyx_report_error(ifnode->token->pos, Error_Waiting_On, "Waiting on static if resolution.");
+                return Symres_Error;
+            } else {
+                return Symres_Yield_Macro;
+            }
+        }
+
+        if (static_if_resolution(ifnode)) {
+            if (ifnode->true_stmt != NULL)  SYMRES(statement, (AstNode **) &ifnode->true_stmt, NULL);
+
+        } else {
+            if (ifnode->false_stmt != NULL) SYMRES(statement, (AstNode **) &ifnode->false_stmt, NULL);
+        }
+
+    } else {
+        if (ifnode->initialization != NULL) {
+            ifnode->scope = scope_create(context.ast_alloc, curr_scope, ifnode->token->pos);
+            scope_enter(ifnode->scope);
+
+            SYMRES(statement_chain, &ifnode->initialization);
+        }
+
+        SYMRES(expression, &ifnode->cond);
+
+        // NOTE: These are statements because "elseif" means the `false_stmt` has an if node.
+        if (ifnode->true_stmt != NULL)  SYMRES(statement, (AstNode **) &ifnode->true_stmt, NULL);
+        if (ifnode->false_stmt != NULL) SYMRES(statement, (AstNode **) &ifnode->false_stmt, NULL);
+
+        if (ifnode->initialization != NULL) scope_leave();
+    }
+
+    return Symres_Success;
+}
+
+static SymresStatus symres_while(AstIfWhile* whilenode) {
+    if (whilenode->initialization != NULL) {
+        whilenode->scope = scope_create(context.ast_alloc, curr_scope, whilenode->token->pos);
+        scope_enter(whilenode->scope);
+
+        SYMRES(statement_chain, &whilenode->initialization);
+    }
+
+    SYMRES(expression, &whilenode->cond);
+
+    if (whilenode->true_stmt)  SYMRES(block, whilenode->true_stmt);
+    if (whilenode->false_stmt) SYMRES(block, whilenode->false_stmt);
+
+    if (whilenode->initialization != NULL) scope_leave();
+
+    return Symres_Success;
+}
+
+static SymresStatus symres_for(AstFor* fornode) {
+    fornode->scope = scope_create(context.ast_alloc, curr_scope, fornode->token->pos);
+    scope_enter(fornode->scope);
+    SYMRES(expression, &fornode->iter);
+    SYMRES(local, &fornode->var);
+    SYMRES(block, fornode->stmt);
+    scope_leave();
+
+    return Symres_Success;
+}
+
+static SymresStatus symres_case(AstSwitchCase *casenode) {
+    if (!casenode->is_default) {
+        bh_arr_each(AstTyped *, expr, casenode->values) {
+            SYMRES(expression, expr);
+        }
+    }
+
+    SYMRES(block, casenode->block);
+    return Symres_Success;
+}
+
+static SymresStatus symres_switch(AstSwitch* switchnode) {
+    if (switchnode->initialization != NULL) {
+        switchnode->scope = scope_create(context.ast_alloc, curr_scope, switchnode->token->pos);
+        scope_enter(switchnode->scope);
+
+        SYMRES(statement_chain, &switchnode->initialization);
+    }
+
+    SYMRES(expression, &switchnode->expr);
+
+    if (switchnode->cases == NULL) {
+        SYMRES(block, switchnode->case_block);
+    } else {
+        bh_arr_each(AstSwitchCase *, pcase, switchnode->cases) {
+            SYMRES(case, *pcase);
+        }
+
+        if (switchnode->default_case) SYMRES(block, switchnode->default_case);
+    }
+
+    if (switchnode->switch_kind == Switch_Kind_Use_Equals && switchnode->case_exprs) {
+        bh_arr_each(CaseToBlock, ctb, switchnode->case_exprs) {
+            SYMRES(expression, (AstTyped **) &ctb->comparison);
+        }
+    }
+
+    if (switchnode->initialization != NULL) scope_leave();
+
+    return Symres_Success;
+}
+
+// CLEANUP: A lot of duplication going on in this function. A proper
+// "namespace" concept would be useful to remove a lot of the fluff
+// code here. There already is try_resolve_symbol_from_node which
+// may be able to do what is needed here?
+static SymresStatus symres_use(AstUse* use) {
+    SYMRES(expression, &use->expr);
+
+    AstTyped *use_expr = (AstTyped *) strip_aliases((AstNode *) use->expr);
+
+    // :EliminatingSymres
+    if (use_expr->kind == Ast_Kind_Package) {
+        AstPackage* package = (AstPackage *) use_expr;
+        SYMRES(package, package);
+
+        if (package->package->scope == curr_scope) return Symres_Success;
+
+        if (use->only == NULL) {
+            OnyxFilePos pos = { 0 };
+            if (use->token != NULL)
+                pos = use->token->pos;
+
+            scope_include(curr_scope, package->package->scope, pos);
+
+        } else {
+            bh_arr_each(QualifiedUse, qu, use->only) {
+                AstNode* thing = symbol_resolve(package->package->scope, qu->symbol_name);
+                if (thing == NULL) { // :SymresStall
+                    if (report_unresolved_symbols) {
+                        onyx_report_error(qu->symbol_name->pos, Error_Critical, 
+                                "The symbol '%b' was not found in this package.",
+                                qu->symbol_name->text, qu->symbol_name->length);
+                        return Symres_Error;
+                    } else {
+                        return Symres_Yield_Macro;
+                    }
+                }
+
+                symbol_introduce(curr_scope, qu->as_name, thing);
+            }
+        }
+
+        if (!use->entity) {
+            add_entities_for_node(NULL, (AstNode *) use, curr_scope, NULL);
+        }
+
+        package_track_use_package(package->package, use->entity);
+        return Symres_Success;
+    }
+
+    if (use_expr->kind == Ast_Kind_Foreign_Block) {
+        AstForeignBlock* fb = (AstForeignBlock *) use_expr;
+        if (fb->entity->state <= Entity_State_Resolve_Symbols) return Symres_Yield_Macro;
+
+        if (fb->scope == curr_scope) return Symres_Success;
+
+        if (use->only == NULL) {
+            OnyxFilePos pos = { 0 };
+            if (use->token != NULL)
+                pos = use->token->pos;
+
+            scope_include(curr_scope, fb->scope, pos);
+
+        } else {
+            bh_arr_each(QualifiedUse, qu, use->only) {
+                AstNode* thing = symbol_resolve(fb->scope, qu->symbol_name);
+                if (thing == NULL) { // :SymresStall
+                    if (report_unresolved_symbols) {
+                        onyx_report_error(qu->symbol_name->pos, Error_Critical, 
+                                "The symbol '%b' was not found in this package.",
+                                qu->symbol_name->text, qu->symbol_name->length);
+                        return Symres_Error;
+                    } else {
+                        return Symres_Yield_Macro;
+                    }
+                }
+
+                symbol_introduce(curr_scope, qu->as_name, thing);
+            }
+        }
+
+        return Symres_Success;
+    }
+
+    if (use_expr->kind == Ast_Kind_Enum_Type) {
+        AstEnumType* et = (AstEnumType *) use_expr;
+
+        bh_arr_each(AstEnumValue *, ev, et->values)
+            symbol_introduce(curr_scope, (*ev)->token, (AstNode *) *ev);
+
+        return Symres_Success;
+    }
+
+    if (use_expr->kind == Ast_Kind_Struct_Type) {
+        AstStructType* st = (AstStructType *) use_expr;
+        if (!st->scope) return Symres_Success;
+
+        if (use->only == NULL) {
+            scope_include(curr_scope, st->scope, use->token->pos);
+
+        } else {
+            bh_arr_each(QualifiedUse, qu, use->only) {
+                AstNode* thing = symbol_resolve(st->scope, qu->symbol_name);
+                if (thing == NULL) {
+                    onyx_report_error(qu->symbol_name->pos, Error_Critical, 
+                            "The symbol '%b' was not found in this scope.",
+                            qu->symbol_name->text, qu->symbol_name->length);
+                    return Symres_Error;
+                }
+
+                symbol_introduce(curr_scope, qu->as_name, thing);
+            }
+        }
+
+        return Symres_Success;
+    }
+
+    if (use_expr->type_node == NULL && use_expr->type == NULL) goto cannot_use;
+
+    // :EliminatingSymres
+    AstType* effective_type = use_expr->type_node;
+    if (effective_type->kind == Ast_Kind_Pointer_Type)
+        effective_type = ((AstPointerType *) effective_type)->elem;
+
+    if (effective_type->kind == Ast_Kind_Struct_Type ||
+            effective_type->kind == Ast_Kind_Poly_Call_Type) {
+
+        if (use_expr->type == NULL)
+            use_expr->type = type_build_from_ast(context.ast_alloc, use_expr->type_node);
+        if (use_expr->type == NULL) goto cannot_use;
+
+        Type* st = use_expr->type;
+        if (st->kind == Type_Kind_Pointer)
+            st = st->Pointer.elem;
+
+        fori (i, 0, shlen(st->Struct.members)) {
+            StructMember* value = st->Struct.members[i].value;
+            AstFieldAccess* fa = make_field_access(context.ast_alloc, use_expr, value->name);
+            symbol_raw_introduce(curr_scope, value->name, use->token->pos, (AstNode *) fa);
+        }
+
+        return Symres_Success;
+    }
+
+cannot_use:
+    onyx_report_error(use->token->pos, Error_Critical, "Cannot use this because its type is unknown.");
+    return Symres_Error;
+}
+
+static SymresStatus symres_directive_solidify(AstDirectiveSolidify** psolid) {
+    AstDirectiveSolidify* solid = *psolid;
+
+    SYMRES(expression, (AstTyped **) &solid->poly_proc);
+    if (solid->poly_proc && solid->poly_proc->kind == Ast_Kind_Directive_Solidify) {
+        AstFunction* potentially_resolved_proc = (AstFunction *) ((AstDirectiveSolidify *) solid->poly_proc)->resolved_proc;
+        if (!potentially_resolved_proc) return Symres_Yield_Micro;
+
+        solid->poly_proc = potentially_resolved_proc;
+    }
+
+    if (!solid->poly_proc || solid->poly_proc->kind != Ast_Kind_Polymorphic_Proc) {
+        onyx_report_error(solid->token->pos, Error_Critical, "Expected polymorphic procedure in #solidify directive.");
+        return Symres_Error;
+    }
+    
+    bh_arr_each(AstPolySolution, sln, solid->known_polyvars) {
+        // HACK: This assumes that 'ast_type' and 'value' are at the same offset.
+        SYMRES(expression, &sln->value);
+    }
+
+    return Symres_Success;
+}
+
+static SymresStatus symres_directive_defined(AstDirectiveDefined** pdefined) {
+    AstDirectiveDefined* defined = *pdefined;
+
+    b32 has_to_be_resolved = context.cycle_almost_detected;
+
+    resolved_a_symbol = 0;
+    SymresStatus ss = symres_expression(&defined->expr);
+    if (has_to_be_resolved && ss != Symres_Success && !resolved_a_symbol) {
+        // The symbol definitely was not found and there is no chance that it could be found.
+        defined->is_defined = 0;
+        return Symres_Success;
+    }
+
+    if (ss == Symres_Success) {
+        defined->is_defined = 1;
+        return Symres_Success;
+    }
+
+    return Symres_Yield_Macro;
+}
+
+static SymresStatus symres_directive_insert(AstDirectiveInsert* insert) {
+    SYMRES(expression, &insert->code_expr);
+    return Symres_Success;
+}
+
+static SymresStatus symres_statement(AstNode** stmt, b32 *remove) {
+    if (remove) *remove = 0;
+
+    switch ((*stmt)->kind) {
+        case Ast_Kind_Return:      SYMRES(return, (AstReturn *) *stmt);                  break;
+        case Ast_Kind_If:          SYMRES(if, (AstIfWhile *) *stmt);                     break;
+        case Ast_Kind_Static_If:   SYMRES(if, (AstIfWhile *) *stmt);                     break;
+        case Ast_Kind_While:       SYMRES(while, (AstIfWhile *) *stmt);                  break;
+        case Ast_Kind_For:         SYMRES(for, (AstFor *) *stmt);                        break;
+        case Ast_Kind_Switch:      SYMRES(switch, (AstSwitch *) *stmt);                  break;
+        case Ast_Kind_Call:        SYMRES(call, (AstCall **) stmt);                      break;
+        case Ast_Kind_Argument:    SYMRES(expression, (AstTyped **) &((AstArgument *) *stmt)->value); break;
+        case Ast_Kind_Block:       SYMRES(block, (AstBlock *) *stmt);                    break;
+        case Ast_Kind_Defer:       SYMRES(statement, &((AstDefer *) *stmt)->stmt, NULL); break;
+        case Ast_Kind_Switch_Case: SYMRES(case, (AstSwitchCase *) *stmt);                break;
+        case Ast_Kind_Jump:        break;
+        case Ast_Kind_Directive_Remove: break;
+
+        case Ast_Kind_Local:
+            // if (remove) *remove = 1;
+            SYMRES(local, (AstLocal **) stmt);
+            break;
+
+        case Ast_Kind_Use:
+            if (remove) *remove = 1;
+            SYMRES(use, (AstUse *) *stmt);
+            break;
+
+        default: SYMRES(expression, (AstTyped **) stmt); break;
+    }
+
+    return Symres_Success;
+}
+
+static SymresStatus symres_statement_chain(AstNode** walker) {
+    b32 remove = 0;
+
+    while (*walker) {
+        SYMRES(statement, walker, &remove);
+        if (remove) {
+            remove = 0;
+            AstNode* tmp = (*walker)->next;
+            (*walker)->next = NULL;
+            (*walker) = tmp;
+
+        } else {
+            walker = &(*walker)->next;
+        }
+    }
+    return Symres_Success;
+}
+
+static SymresStatus symres_block(AstBlock* block) {
+    if (block->rules & Block_Rule_New_Scope) {
+        if (block->scope == NULL)
+            block->scope = scope_create(context.ast_alloc, curr_scope, block->token->pos);
+
+        scope_enter(block->scope);
+    }
+
+    if (block->binding_scope != NULL)
+        scope_include(curr_scope, block->binding_scope, block->token->pos);
+
+    if (block->body) {
+        AstNode** start = &block->body;
+        fori (i, 0, block->statement_idx) {
+            start = &(*start)->next;
+        }
+
+        b32 remove = 0;
+
+        while (*start) {
+            SymresStatus cs = symres_statement(start, &remove);
+
+            if (remove) {
+                remove = 0;
+                AstNode* tmp = (*start)->next;
+                (*start)->next = NULL;
+                (*start) = tmp;
+
+            } else {
+                switch (cs) {
+                    case Symres_Success:
+                        start = &(*start)->next;
+                        block->statement_idx++;
+                        break;
+
+                    default:
+                        return cs;
+                }
+            }
+        }
+
+        block->statement_idx = 0;
+    }
+
+    if (block->rules & Block_Rule_New_Scope)
+        scope_leave();
+
+    return Symres_Success;
+}
+
+SymresStatus symres_function_header(AstFunction* func) {
+    func->flags |= Ast_Flag_Comptime;
+
+    if (func->scope == NULL)
+        func->scope = scope_create(context.ast_alloc, curr_scope, func->token->pos);
+
+    if (func->constraints.constraints != NULL && func->constraints.constraints_met == 0) {
+        bh_arr_each(AstConstraint *, constraint, func->constraints.constraints) {
+            SYMRES(constraint, *constraint);
+        }
+
+        // Return early here to finish checking constraints in the checker.
+        // Will resume here after constraints have been met.
+        return Symres_Success;
+    }
+
+    scope_enter(func->scope);
+
+    bh_arr_each(AstParam, param, func->params) {
+        if (param->default_value != NULL) {
+            SYMRES(expression, &param->default_value);
+            if (onyx_has_errors()) return Symres_Error;
+        }
+    }
+
+    bh_arr_each(AstParam, param, func->params) {
+        symbol_introduce(curr_scope, param->local->token, (AstNode *) param->local);
+    }
+
+    bh_arr_each(AstParam, param, func->params) {
+        if (param->local->type_node != NULL) {
+            SYMRES_INVISIBLE(type, param->local, &param->local->type_node);
+        }
+    }
+
+    if (potentially_convert_function_to_polyproc(func)) {
+        return Symres_Complete;
+    }
+
+    if (func->nodes_that_need_entities_after_clone && bh_arr_length(func->nodes_that_need_entities_after_clone) > 0 && func->entity) {
+        bh_arr_each(AstNode *, node, func->nodes_that_need_entities_after_clone) {
+            // This makes a lot of assumptions about how these nodes are being processed,
+            // and I don't want to start using this with other nodes without considering
+            // what the ramifications of that is.
+            assert((*node)->kind == Ast_Kind_Static_If || (*node)->kind == Ast_Kind_File_Contents);
+
+            // Need to curr_scope->parent because curr_scope is the function body scope.
+            Scope *scope = curr_scope->parent;
+
+            if ((*node)->kind == Ast_Kind_Static_If) {
+                AstIf *static_if = (AstIf *) *node;
+                assert(static_if->defined_in_scope);
+                scope = static_if->defined_in_scope;
+
+                if (func->poly_scope) {
+                    scope = scope_create(context.ast_alloc, scope, static_if->token->pos);
+                    scope_include(scope, func->poly_scope, static_if->token->pos);
+                }
+            }
+
+            add_entities_for_node(NULL, *node, scope, func->entity->package);
+        }
+
+        bh_arr_set_length(func->nodes_that_need_entities_after_clone, 0);
+    }
+
+    SYMRES(type, &func->return_type);
+
+    scope_leave();
+
+    return Symres_Success;
+}
+
+SymresStatus symres_function(AstFunction* func) {
+    if (func->entity_header && func->entity_header->state < Entity_State_Check_Types) return Symres_Yield_Macro;
+    if (func->kind == Ast_Kind_Polymorphic_Proc) return Symres_Complete;
+    assert(func->scope);
+
+    scope_enter(func->scope);
+
+    if ((func->flags & Ast_Flag_Has_Been_Symres) == 0) {
+        // :EliminatingSymres
+        bh_arr_each(AstParam, param, func->params) {
+            // CLEANUP: Currently, in order to 'use' parameters, the type must be completely
+            // resolved and built. This is excessive because all that should need to be known
+            // is the names of the members, since all that happens is implicit field accesses
+            // are placed in the scope. So instead, there should be a way to just query all the
+            // member names in the structure, without needing to know their type. This would be
+            // easy if it were not for 'use' statements in structs. It is made even more complicated
+            // by this situtation:
+            //
+            //     Foo :: struct (T: type_expr) {
+            //         use t : T;
+            //
+            //         something_else := 5 + 6 * 8;
+            //     }
+            //
+            // The 'use t : T' member requires completely knowing the type of T, to know which
+            // members should be brought in. At the moment, that requires completely building the
+            // type of Foo($T).
+            if (param->is_used && !param->use_processed) {
+                if (param->local->type_node != NULL && param->local->type == NULL) {
+                    param->local->type = type_build_from_ast(context.ast_alloc, param->local->type_node);
+
+                    if (param->local->type == NULL) return Symres_Yield_Macro;
+                }
+
+                if (type_is_struct(param->local->type)) {
+                    Type* st;
+                    if (param->local->type->kind == Type_Kind_Struct) {
+                        st = param->local->type;
+                    } else {
+                        st = param->local->type->Pointer.elem;
+                    }
+
+                    if (st->Struct.status != SPS_Uses_Done) return Symres_Yield_Macro;
+
+                    fori (i, 0, shlen(st->Struct.members)) {
+                        StructMember* value = st->Struct.members[i].value;
+                        AstFieldAccess* fa = make_field_access(context.ast_alloc, (AstTyped *) param->local, value->name);
+                        symbol_raw_introduce(curr_scope, value->name, param->local->token->pos, (AstNode *) fa);
+                    }
+
+                    param->use_processed = 1;
+
+                } else if (param->local->type != NULL) {
+                    onyx_report_error(param->local->token->pos, Error_Critical, "Can only 'use' structures or pointers to structures.");
+
+                } else {
+                    // :ExplicitTyping
+                    onyx_report_error(param->local->token->pos, Error_Critical, "Cannot deduce type of parameter '%b'; Try adding it explicitly.",
+                        param->local->token->text,
+                        param->local->token->length);
+                }
+            }
+        }
+
+        bh_arr_each(AstTyped *, pexpr, func->tags) {
+            SYMRES(expression, pexpr);
+        }
+
+        func->flags |= Ast_Flag_Has_Been_Symres;
+    }
+
+    SYMRES(block, func->body);
+
+    scope_leave();
+    return Symres_Success;
+}
+
+static SymresStatus symres_global(AstGlobal* global) {
+    SYMRES(type, &global->type_node);
+    return Symres_Success;
+}
+
+static SymresStatus symres_overloaded_function(AstOverloadedFunction* ofunc) {
+    bh_arr_each(OverloadOption, overload, ofunc->overloads) {
+        SYMRES(expression, &overload->option);
+    }
+    return Symres_Success;
+}
+
+static SymresStatus symres_package(AstPackage* package) {
+    if (package->package == NULL) {
+        if (!package->package_name) return Symres_Error;
+
+        package->package = package_lookup(package->package_name);
+    }
+
+    if (package->package) {
+        return Symres_Success;
+    } else {
+        if (report_unresolved_symbols) {
+            onyx_report_error(package->token->pos, Error_Critical,
+                    "Package '%s' not found in included source files.",
+                    package->package_name);
+            return Symres_Error;
+        } else {
+            return Symres_Yield_Macro;
+        }
+    }
+}
+
+static SymresStatus symres_enum(AstEnumType* enum_node) {
+    if (enum_node->backing->kind == Ast_Kind_Symbol) SYMRES(symbol, (AstNode **) &enum_node->backing);
+    if (enum_node->backing == NULL) return Symres_Error;
+
+    if (enum_node->scope == NULL) {
+        enum_node->backing_type = type_build_from_ast(context.ast_alloc, enum_node->backing);
+        enum_node->scope = scope_create(context.ast_alloc, curr_scope, enum_node->token->pos);
+
+        type_build_from_ast(context.ast_alloc, (AstType *) enum_node);
+    }
+
+    scope_enter(enum_node->scope);
+
+    // :EliminatingSymres
+    u64 next_assign_value = enum_node->is_flags ? 1 : 0;
+    bh_arr_each(AstEnumValue *, value, enum_node->values) {
+        if ((*value)->flags & Ast_Flag_Has_Been_Checked) continue;
+
+        (*value)->type = enum_node->etcache;
+        (*value)->flags |= Ast_Flag_Comptime;
+
+        if ((*value)->value != NULL) {
+            SYMRES(expression, &(*value)->value);
+
+            if ((*value)->value->kind == Ast_Kind_Enum_Value) {
+                (*value)->value = ((AstEnumValue *) (*value)->value)->value;
+                (*value)->value->type = enum_node->etcache;
+            }
+
+            if ((*value)->value->kind == Ast_Kind_NumLit) {
+                AstNumLit *n_value = (AstNumLit *) (*value)->value;
+                resolve_expression_type((AstTyped *) n_value);
+
+                if (type_is_small_integer(n_value->type)) {
+                    next_assign_value = n_value->value.i;
+                } else if (type_is_integer(n_value->type)) {
+                    next_assign_value = n_value->value.l;
+                } else {
+                    onyx_report_error((*value)->token->pos, Error_Critical, "expected numeric integer literal for enum initialization, got '%s'", type_get_name(n_value->type));
+                    return Symres_Error;
+                }
+
+                n_value->type = enum_node->etcache;
+
+            } else {
+                if ((*value)->entity == NULL) {
+                    add_entities_for_node(NULL, (AstNode *) (*value), enum_node->scope, NULL);
+                }
+
+                if (context.cycle_detected) {
+                    onyx_report_error((*value)->token->pos, Error_Critical, "Expected compile time known value for enum initialization.");
+                    return Symres_Error;
+                }
+
+                return Symres_Yield_Macro;
+            }
+
+        } else {
+            AstNumLit* num = make_int_literal(context.ast_alloc, next_assign_value);
+            num->type = enum_node->etcache;
+
+            (*value)->value = (AstTyped *) num;
+        }
+
+        symbol_introduce(enum_node->scope, (*value)->token, (AstNode *) (*value));
+
+        (*value)->flags |= Ast_Flag_Comptime | Ast_Flag_Has_Been_Checked;
+
+        if (enum_node->is_flags) {
+            next_assign_value <<= 1;
+        } else {
+            next_assign_value++;
+        }
+    }
+
+    scope_leave();
+
+    // HACK this ensure that you can only lookup symbols in an Enum that are actually defined in the enum.
+    // However, during the symbol resolution of the values in an enum, they need to be able to see the
+    // enclosing scope.
+    enum_node->scope->parent = NULL;
+
+    return Symres_Success;
+}
+
+static SymresStatus symres_memres_type(AstMemRes** memres) {
+    SYMRES(type, &(*memres)->type_node);
+    return Symres_Success;
+}
+
+static SymresStatus symres_memres(AstMemRes** memres) {
+    if ((*memres)->initial_value != NULL) {
+        SYMRES(expression, &(*memres)->initial_value);
+    }
+    return Symres_Success;
+}
+
+static SymresStatus symres_struct_defaults(AstType* t) {
+    if (t->kind != Ast_Kind_Struct_Type) return Symres_Error;
+
+    AstStructType* st = (AstStructType *) t;
+    if (st->scope) scope_enter(st->scope);
+
+    if (st->meta_tags) {
+        bh_arr_each(AstTyped *, meta, st->meta_tags) {
+            SYMRES(expression, meta);
+        }
+    }
+
+    bh_arr_each(AstStructMember *, smem, st->members) {
+        if ((*smem)->initial_value != NULL) {
+            SYMRES(expression, &(*smem)->initial_value);
+        }
+
+        if ((*smem)->meta_tags != NULL) {
+            bh_arr_each(AstTyped *, meta, (*smem)->meta_tags) {
+                SYMRES(expression, meta);
+            }
+        }
+    }
+
+    if (st->scope) scope_leave();
+    return Symres_Success;
+}
+
+static SymresStatus symres_polyproc(AstFunction* pp) {
+    pp->flags |= Ast_Flag_Comptime;
+    pp->parent_scope_of_poly_proc = curr_scope;
+    return Symres_Success;
+}
+
+static SymresStatus symres_static_if(AstIf* static_if) {
+    if (static_if->flags & Ast_Flag_Dead) return Symres_Complete;
+
+    SYMRES(expression, &static_if->cond);
+    return Symres_Success;
+}
+
+static SymresStatus symres_process_directive(AstNode* directive) {
+    // :EliminatingSymres
+    switch (directive->kind) {
+        case Ast_Kind_Directive_Add_Overload: {
+            AstDirectiveAddOverload *add_overload = (AstDirectiveAddOverload *) directive;
+
+            SYMRES(expression, (AstTyped **) &add_overload->overloaded_function);
+            if (add_overload->overloaded_function == NULL) return Symres_Error; // NOTE: Error message will already be generated
+
+            if (add_overload->overloaded_function->kind != Ast_Kind_Overloaded_Function) {
+                onyx_report_error(add_overload->token->pos, Error_Critical, "#match directive expects a matched procedure.");
+                return Symres_Error;
+            }
+
+            AstOverloadedFunction* ofunc = (AstOverloadedFunction *) add_overload->overloaded_function;
+            if (ofunc->locked) {
+                onyx_report_error(add_overload->token->pos, Error_Critical, "Cannot add match option here as the original #match was declared as #locked.");
+                onyx_report_error(ofunc->token->pos, Error_Critical, "Here is the original #match.");
+                return Symres_Error;
+            }
+
+            if (ofunc->only_local_functions) {
+                if (!token_same_file(add_overload->token, ofunc->token)) {
+                    onyx_report_error(add_overload->token->pos, Error_Critical, "Cannot add match option here as this option is not within the same file as the original #match declared with #local.");
+                    onyx_report_error(ofunc->token->pos, Error_Critical, "Here is the original #match.");
+                }
+            }
+
+            SYMRES(expression, (AstTyped **) &add_overload->overload);
+            add_overload_option(&ofunc->overloads, add_overload->precedence, add_overload->overload);
+            break;
+        }
+
+        case Ast_Kind_Directive_Operator: {
+            AstDirectiveOperator *operator = (AstDirectiveOperator *) directive;
+            SYMRES(expression, &operator->overload);
+            if (!operator->overload) return Symres_Error;
+
+            AstFunction* overload = get_function_from_node((AstNode *) operator->overload);
+            if (overload == NULL) {
+                onyx_report_error(operator->token->pos, Error_Critical, "This cannot be used as an operator overload.");
+                return Symres_Error;
+            }
+
+            if (operator->operator != Binary_Op_Subscript_Equals && bh_arr_length(overload->params) != 2) {
+                onyx_report_error(operator->token->pos, Error_Critical, "Expected exactly 2 arguments for binary operator overload.");
+                return Symres_Error;
+            }
+
+            add_overload_option(&operator_overloads[operator->operator], 0, operator->overload);
+            break;
+        }
+
+        case Ast_Kind_Directive_Export: {
+            AstDirectiveExport *export = (AstDirectiveExport *) directive;
+            SYMRES(expression, &export->export);
+            SYMRES(expression, &export->export_name_expr);
+
+            if (export->export->kind == Ast_Kind_Polymorphic_Proc) {
+                onyx_report_error(export->token->pos, Error_Critical, "Cannot export a polymorphic function.");
+                return Symres_Error;
+            }
+
+            if (export->export->kind == Ast_Kind_Function) {
+                AstFunction *func = (AstFunction *) export->export;
+                func->exported_name = export->export_name;
+                func->is_exported = 1;
+
+                if (func->is_exported) {
+                    if (func->is_foreign) {
+                        onyx_report_error(export->token->pos, Error_Critical, "Cannot export a foreign function.");
+                        return Symres_Error;
+                    }
+
+                    if (func->is_intrinsic) {
+                        onyx_report_error(export->token->pos, Error_Critical, "Cannot export an intrinsic function.");
+                        return Symres_Error;
+                    }
+                }
+            }
+
+            break;
+        }
+
+        case Ast_Kind_Directive_Init: {
+            AstDirectiveInit *init = (AstDirectiveInit *) directive;
+            SYMRES(expression, &init->init_proc);
+
+            if (init->dependencies) {
+                bh_arr_each(AstDirectiveInit *, dependency, init->dependencies) {
+                    SYMRES(expression, (AstTyped **) dependency);
+                }
+            }
+
+            break;
+        }
+
+        case Ast_Kind_Directive_Library: {
+            AstDirectiveLibrary *library = (AstDirectiveLibrary *) directive;
+            SYMRES(expression, &library->library_symbol);
+            break;
+        }
+
+        case Ast_Kind_Injection: {
+            AstInjection *inject = (AstInjection *) directive;
+
+            if (inject->dest == NULL) {
+                if (inject->full_loc == NULL) return Symres_Error;
+
+                if (inject->full_loc->kind != Ast_Kind_Field_Access) {
+                    onyx_report_error(inject->token->pos, Error_Critical, "#inject expects a dot (a.b) expression for the injection point.");
+                    return Symres_Error;
+                }
+
+                AstFieldAccess *acc = (AstFieldAccess *) inject->full_loc;
+                inject->dest = acc->expr;
+                inject->symbol = acc->token;
+            }
+
+            SYMRES(expression, &inject->dest);
+            SYMRES(expression, &inject->to_inject);
+
+            Scope *scope = get_scope_from_node_or_create((AstNode *) inject->dest);
+            if (scope == NULL) {
+                onyx_report_error(inject->token->pos, Error_Critical, "Cannot #inject here.");
+                return Symres_Error;
+            }
+
+            AstBinding *binding = onyx_ast_node_new(context.ast_alloc, sizeof(AstBinding), Ast_Kind_Binding);
+            binding->token = inject->symbol;
+            binding->node = (AstNode *) inject->to_inject;
+
+            Package *pac = NULL;
+            if (inject->dest->kind == Ast_Kind_Package) {
+                pac = ((AstPackage *) inject->dest)->package;
+            }
+
+            add_entities_for_node(NULL, (AstNode *) binding, scope, pac);
+            return Symres_Complete;
+        }
+    }
+
+    return Symres_Success;
+}
+
+static SymresStatus symres_macro(AstMacro* macro) {
+    macro->flags |= Ast_Flag_Comptime;
+
+    if (macro->body->kind == Ast_Kind_Function) {
+        SYMRES(function_header, (AstFunction *) macro->body);
+    }
+    else if (macro->body->kind == Ast_Kind_Polymorphic_Proc) {
+        SYMRES(polyproc, (AstFunction *) macro->body);
+    }
+
+    return Symres_Success;
+}
+
+static SymresStatus symres_constraint(AstConstraint* constraint) {
+    switch (constraint->phase) {
+        case Constraint_Phase_Cloning_Expressions:
+        case Constraint_Phase_Waiting_To_Be_Queued: {
+            SYMRES(expression, (AstTyped **) &constraint->interface);
+
+            bh_arr_each(AstType *, type_arg, constraint->type_args) {
+                SYMRES(type, type_arg);
+            }
+
+            return Symres_Success;
+        }
+
+        case Constraint_Phase_Checking_Expressions: {
+            fori (i, constraint->expr_idx, bh_arr_length(constraint->exprs)) {
+                SYMRES(expression, &constraint->exprs[i].expr);
+
+                if (constraint->exprs[i].expected_type_expr) {
+                    SYMRES(type, &constraint->exprs[i].expected_type_expr);
+                }
+            }
+
+            return Symres_Success;
+        }
+    }
+
+    return Symres_Success;
+}
+
+static SymresStatus symres_polyquery(AstPolyQuery *query) {
+    // :EliminatingSymres
+    query->successful_symres = 0;
+
+    if (query->function_header->scope == NULL)
+        query->function_header->scope = scope_create(context.ast_alloc, query->proc->parent_scope_of_poly_proc, query->token->pos);
+
+    scope_enter(query->function_header->scope);
+
+    u32 idx = 0;
+    bh_arr_each(AstParam, param, query->function_header->params) {
+        bh_arr_each(AstPolyParam, pp, query->proc->poly_params) {
+            if (pp->kind == PPK_Baked_Value && pp->idx == idx) goto skip_introducing_symbol;
+        }
+
+        symbol_introduce(curr_scope, param->local->token, (AstNode *) param->local);
+
+    skip_introducing_symbol:
+        idx++;
+    }
+
+    bh_arr_each(AstParam, param, query->function_header->params) {
+        if (param->local->type_node != NULL) {
+            resolved_a_symbol = 0;
+
+            param->local->flags |= Ast_Flag_Symbol_Invisible;
+            symres_type(&param->local->type_node);
+            param->local->flags &= ~Ast_Flag_Symbol_Invisible;
+
+            onyx_clear_errors();
+
+            if (resolved_a_symbol) query->successful_symres = 1;
+        }
+    }
+
+    scope_leave();
+    return Symres_Success;
+}
+
+static SymresStatus symres_foreign_block(AstForeignBlock *fb) {
+    if (fb->scope == NULL)
+        fb->scope = scope_create(context.ast_alloc, curr_scope, fb->token->pos);
+
+    bh_arr_each(Entity *, pent, fb->captured_entities) {
+        Entity *ent = *pent;
+        if (ent->type == Entity_Type_Function_Header) {
+            if (ent->function->body->next != NULL) {
+                onyx_report_error(ent->function->token->pos, Error_Critical, "Procedures declared in a #foreign block should not have bodies.");
+                return Symres_Error;
+            }
+
+            ent->function->foreign_name = ent->function->intrinsic_name; // Hmm... This might not be right?
+            ent->function->foreign_module = fb->module_name;
+            ent->function->is_foreign = 1;
+            ent->function->entity = NULL;
+            ent->function->entity_header = NULL;
+            ent->function->entity_body = NULL;
+
+            add_entities_for_node(NULL, (AstNode *) ent->function, ent->scope, ent->package);
+            continue;
+        }
+
+        if (ent->type == Entity_Type_Binding) {
+            AstBinding* new_binding = onyx_ast_node_new(context.ast_alloc, sizeof(AstBinding), Ast_Kind_Binding);
+            new_binding->token = ent->binding->token;
+            new_binding->node = ent->binding->node;
+
+            Entity e;
+            memset(&e, 0, sizeof(e));
+            e.type = Entity_Type_Binding;
+            e.state = Entity_State_Introduce_Symbols;
+            e.binding = new_binding;
+            e.scope = fb->scope;
+            e.package = ent->package;
+
+            entity_heap_insert(&context.entities, e);
+        }
+
+        if (ent->type != Entity_Type_Function) {
+            entity_heap_insert_existing(&context.entities, ent);
+        }
+    }
+
+    return Symres_Complete;
+}
+
+static SymresStatus symres_include(AstInclude* include) {
+    if (include->name != NULL) return Symres_Goto_Parse;
+
+    SYMRES(expression, &include->name_node);
+
+    if (include->name_node->kind != Ast_Kind_StrLit) {
+        onyx_report_error(include->token->pos, Error_Critical, "Expected compile-time known string literal here. Got '%s'.", onyx_ast_node_kind_string(include->name_node->kind));
+        return Symres_Error;
+    }
+
+    OnyxToken* str_token = include->name_node->token;
+    if (str_token != NULL) {
+        token_toggle_end(str_token);
+        include->name = bh_strdup(context.ast_alloc, str_token->text);
+        string_process_escape_seqs(include->name, include->name, strlen(include->name));
+        token_toggle_end(str_token);
+    }
+
+    return Symres_Goto_Parse;
+}
+
+static SymresStatus symres_file_contents(AstFileContents* fc) {
+    SYMRES(expression, &fc->filename_expr);
+
+    if (fc->filename_expr->kind != Ast_Kind_StrLit) {
+        onyx_report_error(fc->token->pos, Error_Critical, "Expected given expression to be a compile-time stirng literal.");
+        return Symres_Error;
+    }
+
+    return Symres_Success;
+}
+
+void symres_entity(Entity* ent) {
+    if (ent->scope) scope_enter(ent->scope);
+
+    report_unresolved_symbols = context.cycle_detected;
+
+    SymresStatus ss = Symres_Success;
+    EntityState next_state = Entity_State_Check_Types;
+
+    switch (ent->type) {
+        case Entity_Type_Binding: {
+            symbol_introduce(curr_scope, ent->binding->token, ent->binding->node);
+            package_reinsert_use_packages(ent->package);
+            next_state = Entity_State_Finalized;
+            break;
+        }
+
+        case Entity_Type_Static_If:               ss = symres_static_if(ent->static_if); break;
+
+        case Entity_Type_Load_Path:
+        case Entity_Type_Load_File:               ss = symres_include(ent->include); break;
+        case Entity_Type_File_Contents:           ss = symres_file_contents(ent->file_contents); break;
+
+        case Entity_Type_Foreign_Function_Header:
+        case Entity_Type_Temp_Function_Header:
+        case Entity_Type_Function_Header:         ss = symres_function_header(ent->function); break;
+        case Entity_Type_Function:                ss = symres_function(ent->function);        break;
+
+        case Entity_Type_Global_Header:           ss = symres_global(ent->global); break;
+
+        case Entity_Type_Use_Package:
+        case Entity_Type_Use:                     ss = symres_use(ent->use);
+                                                  next_state = Entity_State_Finalized;
+                                                  break;
+
+        case Entity_Type_Polymorphic_Proc:        ss = symres_polyproc(ent->poly_proc);
+                                                  next_state = Entity_State_Finalized;
+                                                  break;
+
+        case Entity_Type_Overloaded_Function:     ss = symres_overloaded_function(ent->overloaded_function); break;
+        case Entity_Type_Expression:              ss = symres_expression(&ent->expr); break;
+        case Entity_Type_Type_Alias:              ss = symres_type(&ent->type_alias); break;
+        case Entity_Type_Enum:                    ss = symres_enum(ent->enum_type); break;
+        case Entity_Type_Memory_Reservation_Type: ss = symres_memres_type(&ent->mem_res); break;
+        case Entity_Type_Memory_Reservation:      ss = symres_memres(&ent->mem_res); break;
+        case Entity_Type_String_Literal:          ss = symres_expression(&ent->expr); break;
+        case Entity_Type_Struct_Member_Default:   ss = symres_struct_defaults((AstType *) ent->type_alias); break;
+        case Entity_Type_Process_Directive:       ss = symres_process_directive((AstNode *) ent->expr); break;
+        case Entity_Type_Macro:                   ss = symres_macro(ent->macro); break;
+        case Entity_Type_Constraint_Check:        ss = symres_constraint(ent->constraint); break;
+        case Entity_Type_Polymorph_Query:         ss = symres_polyquery(ent->poly_query); break;
+        case Entity_Type_Foreign_Block:           ss = symres_foreign_block(ent->foreign_block);
+                                                  if (context.options->generate_foreign_info) {
+                                                      next_state = Entity_State_Check_Types;
+                                                      ss = Symres_Success;
+                                                  }
+                                                  break;
+
+        default: break;
+    }
+
+    if (ss == Symres_Yield_Macro) ent->macro_attempts++;
+    if (ss == Symres_Yield_Micro) ent->micro_attempts++;
+    if (ss == Symres_Complete)    ent->state = Entity_State_Finalized;
+    if (ss == Symres_Goto_Parse)  ent->state = Entity_State_Parse;
+    if (ss == Symres_Success) {
+        ent->macro_attempts = 0;
+        ent->micro_attempts = 0;
+        ent->state = next_state;
+    }
+
+    curr_scope = NULL;
+}
diff --git a/compiler/src/types.c b/compiler/src/types.c
new file mode 100644 (file)
index 0000000..0d9568a
--- /dev/null
@@ -0,0 +1,1490 @@
+#define BH_DEBUG
+#include "stb_ds.h"
+#include "types.h"
+#include "astnodes.h"
+#include "utils.h"
+#include "errors.h"
+
+// NOTE: These have to be in the same order as Basic
+Type basic_types[] = {
+    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_void, { Basic_Kind_Void,                    0,                       0,  1, "void"   } },
+
+    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_bool, { Basic_Kind_Bool,   Basic_Flag_Boolean,                       1,  1, "bool"   } },
+
+    { Type_Kind_Basic, 0, 0, NULL,                        { Basic_Kind_Int_Unsized, Basic_Flag_Integer,                  0,  0, "unsized int" } },
+    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_i8,  { Basic_Kind_I8,     Basic_Flag_Integer,                       1,  1, "i8"     } },
+    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_u8,  { Basic_Kind_U8,     Basic_Flag_Integer | Basic_Flag_Unsigned, 1,  1, "u8"     } },
+    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_i16, { Basic_Kind_I16,    Basic_Flag_Integer,                       2,  2, "i16"    } },
+    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_u16, { Basic_Kind_U16,    Basic_Flag_Integer | Basic_Flag_Unsigned, 2,  2, "u16"    } },
+    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_i32, { Basic_Kind_I32,    Basic_Flag_Integer,                       4,  4, "i32"    } },
+    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_u32, { Basic_Kind_U32,    Basic_Flag_Integer | Basic_Flag_Unsigned, 4,  4, "u32"    } },
+    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_i64, { Basic_Kind_I64,    Basic_Flag_Integer,                       8,  8, "i64"    } },
+    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_u64, { Basic_Kind_U64,    Basic_Flag_Integer | Basic_Flag_Unsigned, 8,  8, "u64"    } },
+
+    { Type_Kind_Basic, 0, 0, NULL,                        { Basic_Kind_Float_Unsized, Basic_Flag_Float,                  0,  0, "unsized float" } },
+    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_f32, { Basic_Kind_F32,    Basic_Flag_Float,                         4,  4, "f32"    } },
+    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_f64, { Basic_Kind_F64,    Basic_Flag_Float,                         8,  4, "f64"    } },
+
+    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_rawptr, { Basic_Kind_Rawptr, Basic_Flag_Pointer,                    POINTER_SIZE,  POINTER_SIZE, "rawptr" } },
+
+    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_i8x16, { Basic_Kind_I8X16,  Basic_Flag_SIMD,                        16, 16, "i8x16" } },
+    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_i16x8, { Basic_Kind_I16X8,  Basic_Flag_SIMD,                        16, 16, "i16x8" } },
+    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_i32x4, { Basic_Kind_I32X4,  Basic_Flag_SIMD,                        16, 16, "i32x4" } },
+    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_i64x2, { Basic_Kind_I64X2,  Basic_Flag_SIMD,                        16, 16, "i64x2" } },
+    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_f32x4, { Basic_Kind_F32X4,  Basic_Flag_SIMD,                        16, 16, "f32x4" } },
+    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_f64x2, { Basic_Kind_F64X2,  Basic_Flag_SIMD,                        16, 16, "f64x2" } },
+    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_v128,  { Basic_Kind_V128,   Basic_Flag_SIMD,                        16, 16, "v128"  } },
+
+    { Type_Kind_Basic, 0, 0, NULL,                          { Basic_Kind_Type_Index, Basic_Flag_Type_Index,              4,  4, "type_expr" } },
+};
+
+// TODO: Document this!!
+       bh_imap type_map;
+static bh_imap type_pointer_map;
+static bh_imap type_array_map;
+static bh_imap type_slice_map;
+static bh_imap type_dynarr_map;
+static bh_imap type_vararg_map;
+static Table(u64) type_func_map;
+
+static Type* type_create(TypeKind kind, bh_allocator a, u32 extra_type_pointer_count) {
+    Type* type = bh_alloc(a, sizeof(Type) + sizeof(Type *) * extra_type_pointer_count);
+    type->kind = kind;
+    type->ast_type = NULL;
+    return type;
+}
+
+static void type_register(Type* type) {
+    static u32 next_unique_id = 1;
+    type->id = next_unique_id++;
+    if (type->ast_type) type->ast_type->type_id = type->id;
+
+    bh_imap_put(&type_map, type->id, (u64) type);
+}
+
+void types_init() {
+    bh_imap_init(&type_map,         global_heap_allocator, 255);
+    bh_imap_init(&type_pointer_map, global_heap_allocator, 255);
+    bh_imap_init(&type_array_map,   global_heap_allocator, 255);
+    bh_imap_init(&type_slice_map,   global_heap_allocator, 255);
+    bh_imap_init(&type_dynarr_map,  global_heap_allocator, 255);
+    bh_imap_init(&type_vararg_map,  global_heap_allocator, 255);
+    sh_new_arena(type_func_map);
+
+    fori (i, 0, Basic_Kind_Count) type_register(&basic_types[i]);
+}
+
+void types_dump_type_info() {
+    bh_arr_each(bh__imap_entry, entry, type_map.entries) {
+        bh_printf("%d -> %s\n", entry->key, type_get_name((Type *) entry->value));
+    }
+}
+
+b32 types_are_compatible_(Type* t1, Type* t2, b32 recurse_pointers) {
+    // NOTE: If they are pointing to the same thing,
+    // it is safe to assume they are the same type
+    if (t1 == t2) return 1;
+    if (t1 == NULL || t2 == NULL) return 0;
+    if (t1->id == t2->id) return 1;
+
+    if (t1 == &type_auto_return || t2 == &type_auto_return) {
+        return 0;
+    }
+
+    switch (t1->kind) {
+        case Type_Kind_Basic:
+            if (t2->kind == Type_Kind_Basic) {
+                // Signedness of an integer doesn't matter.
+                if ((t1->Basic.flags & Basic_Flag_Integer) && (t2->Basic.flags & Basic_Flag_Integer)) {
+                    return t1->Basic.size == t2->Basic.size;
+                }
+
+                if (t1->Basic.kind == Basic_Kind_V128 || t2->Basic.kind == Basic_Kind_V128) return 1;
+            }
+
+            if (t1->Basic.kind == Basic_Kind_Rawptr && type_is_pointer(t2)) {
+                return 1;
+            }
+            break;
+
+        case Type_Kind_Pointer: {
+            if (t2->kind == Type_Kind_Pointer) {
+                if (!recurse_pointers) return 1;
+
+                if (types_are_compatible(t1->Pointer.elem, t2->Pointer.elem)) return 1;
+
+                if (t1->Pointer.elem->kind == Type_Kind_Struct && t2->Pointer.elem->kind == Type_Kind_Struct) {
+                    Type* t1_struct = t1->Pointer.elem;
+                    Type* t2_struct = t2->Pointer.elem;
+
+                    bh_arr(StructMember *) members = t1_struct->Struct.memarr;
+                    if (bh_arr_length(members) > 0 && members[0]->used)
+                        return types_are_compatible(t2_struct,members[0]->type);
+                }
+            }
+
+            if (t2->kind == Type_Kind_Basic && t2->Basic.kind == Basic_Kind_Rawptr) return 1;
+
+            break;
+        }
+
+        case Type_Kind_Array: {
+            if (t2->kind != Type_Kind_Array) return 0;
+
+            if (t1->Array.count != 0)
+                if (t1->Array.count != t2->Array.count) return 0;
+
+            return types_are_compatible(t1->Array.elem, t2->Array.elem);
+        }
+
+        case Type_Kind_Struct: {
+            // NOTE: The check above for t1 == t2 would already catch this.
+
+            // if (t2->kind != Type_Kind_Struct) return 0;
+            // if (t1->Struct.unique_id != t2->Struct.unique_id) return 0;
+            // if (t1->Struct.mem_count != t2->Struct.mem_count) return 0;
+            return 0;
+        }
+
+        case Type_Kind_Enum: {
+            // NOTE: The check above for t1 == t2 would already catch this.
+            return 0;
+        }
+
+        case Type_Kind_Function: {
+            if (t2->kind != Type_Kind_Function) return 0;
+            if (t1->Function.param_count != t2->Function.param_count) return 0;
+
+            if (!types_are_compatible(t1->Function.return_type, t2->Function.return_type)) return 0;
+
+            if (t1->Function.param_count > 0) {
+                fori (i, 0, t1->Function.param_count) {
+                    if (!types_are_compatible(t1->Function.params[i], t2->Function.params[i])) return 0;
+                }
+            }
+
+            return 1;
+        }
+
+        case Type_Kind_Slice: {
+            if (t2->kind != Type_Kind_Slice) return 0;
+            return types_are_compatible(t1->Slice.elem, t2->Slice.elem);
+        }
+
+        case Type_Kind_VarArgs: {
+            if (t2->kind != Type_Kind_VarArgs) return 0;
+            return types_are_compatible(t1->VarArgs.elem, t2->VarArgs.elem);
+        }
+
+        case Type_Kind_DynArray: {
+            if (t2->kind != Type_Kind_DynArray) return 0;
+            return types_are_compatible(t1->DynArray.elem, t2->DynArray.elem);
+        }
+
+        case Type_Kind_Compound: {
+            if (t2->kind != Type_Kind_Compound) return 0;
+            if (t1->Compound.count != t2->Compound.count) return 0;
+
+            fori (i, 0, (i64) t1->Compound.count) {
+                if (!types_are_compatible(t1->Compound.types[i], t2->Compound.types[i])) return 0;
+            }
+
+            return 1;
+        }
+
+        case Type_Kind_Distinct:
+            // If the above cases didn't catch it, then these distinct types are not compatible.
+            return 0;
+
+        default:
+            assert(("Invalid type", 0));
+            break;
+    }
+
+    return 0;
+}
+
+b32 types_are_compatible(Type* t1, Type* t2) {
+    return types_are_compatible_(t1, t2, 1);
+}
+
+u32 type_size_of(Type* type) {
+    if (type == NULL) return 0;
+
+    switch (type->kind) {
+        case Type_Kind_Basic:    return type->Basic.size;
+        case Type_Kind_Pointer:  return POINTER_SIZE;
+        case Type_Kind_Function: return 4;
+        case Type_Kind_Array:    return type->Array.size;
+        case Type_Kind_Struct:   return type->Struct.size;
+        case Type_Kind_Enum:     return type_size_of(type->Enum.backing);
+        case Type_Kind_Slice:    return POINTER_SIZE * 2; // HACK: These should not have to be 16 bytes in size, they should only have to be 12,
+        case Type_Kind_VarArgs:  return POINTER_SIZE * 2; // but there are alignment issues right now with that so I decided to not fight it and just make them 16 bytes in size.
+        case Type_Kind_DynArray: return POINTER_SIZE + 8 + 2 * POINTER_SIZE; // data (8), count (4), capacity (4), allocator { func (4), ---(4), data (8) }
+        case Type_Kind_Compound: return type->Compound.size;
+        case Type_Kind_Distinct: return type_size_of(type->Distinct.base_type);
+        default:                 return 0;
+    }
+}
+
+u32 type_alignment_of(Type* type) {
+    if (type == NULL) return 1;
+
+    switch (type->kind) {
+        case Type_Kind_Basic:    return type->Basic.alignment;
+        case Type_Kind_Pointer:  return POINTER_SIZE;
+        case Type_Kind_Function: return 4;
+        case Type_Kind_Array:    return type_alignment_of(type->Array.elem);
+        case Type_Kind_Struct:   return type->Struct.alignment;
+        case Type_Kind_Enum:     return type_alignment_of(type->Enum.backing);
+        case Type_Kind_Slice:    return POINTER_SIZE;
+        case Type_Kind_VarArgs:  return POINTER_SIZE;
+        case Type_Kind_DynArray: return POINTER_SIZE;
+        case Type_Kind_Compound: return 4; // HACK
+        case Type_Kind_Distinct: return type_alignment_of(type->Distinct.base_type);
+        default: return 1;
+    }
+}
+
+// If this function returns NULL, then the caller MUST yield because the type may still be constructed in the future.
+// If there was an error constructing the type, then this function will report that directly.
+Type* type_build_from_ast(bh_allocator alloc, AstType* type_node) {
+    if (type_node == NULL) return NULL;
+
+    switch (type_node->kind) {
+        case Ast_Kind_Pointer_Type: {
+            // ((AstPointerType *) type_node)->elem->flags |= type_node->flags & Ast_Flag_Header_Check_No_Error;
+            Type* ptr_type = type_make_pointer(alloc, type_build_from_ast(alloc, ((AstPointerType *) type_node)->elem));
+            if (ptr_type) ptr_type->ast_type = type_node;
+            return ptr_type;
+        }
+
+        case Ast_Kind_Function_Type: {
+            AstFunctionType* ftype_node = (AstFunctionType *) type_node;
+            u64 param_count = ftype_node->param_count;
+
+            Type* return_type = type_build_from_ast(alloc, ftype_node->return_type);
+            if (return_type == NULL) return NULL;
+
+            Type* func_type = type_create(Type_Kind_Function, alloc, param_count);
+            func_type->ast_type = type_node;
+            func_type->Function.param_count = param_count;
+            func_type->Function.needed_param_count = param_count;
+            func_type->Function.vararg_arg_pos = -1;
+            func_type->Function.return_type = return_type;
+
+            if (param_count > 0) {
+                fori (i, 0, (i64) param_count) {
+                    func_type->Function.params[i] = type_build_from_ast(alloc, ftype_node->params[i]);
+
+                    // LEAK LEAK LEAK
+                    if (func_type->Function.params[i] == NULL) return NULL;
+                }
+            }
+
+            char* name = (char *) type_get_unique_name(func_type);
+            if (func_type->Function.return_type != &type_auto_return) {
+                i32 index = shgeti(type_func_map, name);
+                if (index != -1) {
+                    u64 id = type_func_map[index].value;
+                    Type* existing_type = (Type *) bh_imap_get(&type_map, id);
+
+                    // LEAK LEAK LEAK the func_type that is created
+                    return existing_type;
+                }
+            }
+
+            type_register(func_type);
+            shput(type_func_map, name, func_type->id);
+
+            return func_type;
+        }
+
+        case Ast_Kind_Array_Type: {
+            AstArrayType* a_node = (AstArrayType *) type_node;
+
+            Type *elem_type = type_build_from_ast(alloc, a_node->elem);
+            if (elem_type == NULL)  return NULL;
+
+            u32 count = 0;
+            if (a_node->count_expr) {
+                if (a_node->count_expr->type == NULL)
+                    a_node->count_expr->type = type_build_from_ast(alloc, a_node->count_expr->type_node);
+
+                if (node_is_auto_cast((AstNode *) a_node->count_expr)) {
+                    a_node->count_expr = ((AstUnaryOp *) a_node)->expr;
+                }
+
+                resolve_expression_type(a_node->count_expr);
+
+                // NOTE: Currently, the count_expr has to be an I32 literal
+                if (a_node->count_expr->type->kind != Type_Kind_Basic
+                    || a_node->count_expr->type->Basic.kind != Basic_Kind_I32) {
+                    onyx_report_error(type_node->token->pos, Error_Critical, "Array type expects type 'i32' for size, got '%s'.",
+                        type_get_name(a_node->count_expr->type));
+                    return NULL;
+                }
+
+                count = get_expression_integer_value(a_node->count_expr, NULL);
+            }
+
+            Type* array_type = type_make_array(alloc, elem_type, count);
+            if (array_type) array_type->ast_type = type_node;
+            return array_type;
+        }
+
+        case Ast_Kind_Struct_Type: {
+            AstStructType* s_node = (AstStructType *) type_node;
+            if (s_node->stcache != NULL) return s_node->stcache;
+            if (s_node->pending_type != NULL && s_node->pending_type_is_valid) return s_node->pending_type;
+            if (!s_node->ready_to_build_type) return NULL;
+
+            Type* s_type;
+            if (s_node->pending_type == NULL) {
+                s_type = type_create(Type_Kind_Struct, alloc, 0);
+                s_node->pending_type = s_type;
+
+                s_type->ast_type = type_node;
+                s_type->Struct.name = s_node->name;
+                s_type->Struct.mem_count = bh_arr_length(s_node->members);
+                s_type->Struct.meta_tags = s_node->meta_tags;
+                s_type->Struct.constructed_from = NULL;
+                s_type->Struct.status = SPS_Start;
+                type_register(s_type);
+
+                s_type->Struct.memarr = NULL;
+                sh_new_arena(s_type->Struct.members);
+                bh_arr_new(global_heap_allocator, s_type->Struct.memarr, s_type->Struct.mem_count);
+
+            } else {
+                s_type = s_node->pending_type;
+            }
+
+            s_type->Struct.poly_sln = NULL;
+
+            bh_arr_clear(s_type->Struct.memarr);
+            shfree(s_type->Struct.members);
+            sh_new_arena(s_type->Struct.members);
+
+            s_node->pending_type_is_valid = 1;
+
+            b32 is_union = s_node->is_union;
+            u32 size = 0;
+            u32 offset = 0;
+            u32 alignment = 1, mem_alignment;
+            u32 idx = 0;
+            bh_arr_each(AstStructMember *, member, s_node->members) {
+                if ((*member)->type == NULL)
+                    (*member)->type = type_build_from_ast(alloc, (*member)->type_node);
+
+                if ((*member)->type == NULL) {
+                    s_node->pending_type_is_valid = 0;
+                    return NULL;
+                }
+
+                mem_alignment = type_alignment_of((*member)->type);
+                if (mem_alignment <= 0) {
+                    onyx_report_error((*member)->token->pos, Error_Critical, "Invalid member type: %s. Has alignment %d", type_get_name((*member)->type), mem_alignment); 
+                    return NULL;
+                }
+
+                if (mem_alignment > alignment) alignment = mem_alignment;
+
+                if (!s_node->is_packed) {
+                    bh_align(offset, mem_alignment);
+                }
+
+                token_toggle_end((*member)->token);
+                if (shgeti(s_type->Struct.members, (*member)->token->text) != -1) {
+                    onyx_report_error((*member)->token->pos, Error_Critical, "Duplicate struct member, '%s'.", (*member)->token->text);
+                    return NULL;
+                }
+
+                StructMember* smem = bh_alloc_item(alloc, StructMember);
+                smem->offset = offset;
+                smem->type = (*member)->type;
+                smem->idx = idx;
+                smem->name = bh_strdup(alloc, (*member)->token->text);
+                smem->token = (*member)->token;
+                smem->initial_value = &(*member)->initial_value;
+                smem->meta_tags = (*member)->meta_tags;
+
+                smem->included_through_use = 0;
+                smem->used = (*member)->is_used;
+                smem->use_through_pointer_index = -1;
+                shput(s_type->Struct.members, (*member)->token->text, smem);
+                bh_arr_push(s_type->Struct.memarr, smem);
+                token_toggle_end((*member)->token);
+
+                u32 type_size = type_size_of((*member)->type);
+
+                if (!is_union) {
+                    offset += type_size;
+                    size = offset;
+                } else {
+                    size = bh_max(size, type_size);
+                }
+
+                idx++;
+            }
+
+            u32 min_alignment = get_expression_integer_value(s_node->min_alignment_, NULL);
+            alignment = bh_max(min_alignment, alignment);
+            if (!s_node->is_packed) {
+                bh_align(size, alignment);
+            }
+
+            u32 min_size = get_expression_integer_value(s_node->min_size_, NULL);
+            size = bh_max(min_size, size);
+
+            s_type->Struct.alignment = alignment;
+            s_type->Struct.size = size;
+
+            s_type->Struct.linear_members = NULL;
+            bh_arr_new(global_heap_allocator, s_type->Struct.linear_members, s_type->Struct.mem_count);
+            build_linear_types_with_offset(s_type, &s_type->Struct.linear_members, 0);
+
+            s_type->Struct.status = SPS_Members_Done;
+            return s_type;
+        }
+
+        case Ast_Kind_Enum_Type: {
+            AstEnumType* enum_node = (AstEnumType *) type_node;
+            if (enum_node->etcache) return enum_node->etcache;
+            if (enum_node->backing_type == NULL) return NULL;
+
+            Type* enum_type = type_create(Type_Kind_Enum, alloc, 0);
+            enum_node->etcache = enum_type;
+
+            enum_type->ast_type = type_node;
+            enum_type->Enum.backing = enum_node->backing_type;
+            enum_type->Enum.name = enum_node->name;
+            enum_type->Enum.is_flags = enum_node->is_flags;
+
+            type_register(enum_type);
+            return enum_type;
+        }
+
+        case Ast_Kind_Slice_Type: {
+            Type* slice_type = type_make_slice(alloc, type_build_from_ast(alloc, ((AstSliceType *) type_node)->elem));
+            if (slice_type) slice_type->ast_type = type_node;
+            return slice_type;
+        }
+
+        case Ast_Kind_DynArr_Type: {
+            Type* dynarr_type = type_make_dynarray(alloc, type_build_from_ast(alloc, ((AstDynArrType *) type_node)->elem));
+            if (dynarr_type) dynarr_type->ast_type = type_node;
+            return dynarr_type;
+        }
+
+        case Ast_Kind_VarArg_Type: {
+            Type* va_type = type_make_varargs(alloc, type_build_from_ast(alloc, ((AstVarArgType *) type_node)->elem));   
+            if (va_type) va_type->ast_type = type_node;
+            return va_type;
+        }
+
+        case Ast_Kind_Basic_Type: {
+            return ((AstBasicType *) type_node)->basic_type;
+        }
+
+        case Ast_Kind_Type_Alias: {
+            Type* type = type_build_from_ast(alloc, ((AstTypeAlias *) type_node)->to);
+            if (type && type->ast_type) type_node->type_id = type->id;
+            return type;
+        }
+
+        case Ast_Kind_Type_Raw_Alias:
+            return ((AstTypeRawAlias *) type_node)->to;
+
+        case Ast_Kind_Poly_Struct_Type: {
+            if (type_node->type_id != 0) return NULL;
+
+            Type* p_type = type_create(Type_Kind_PolyStruct, alloc, 0);
+            p_type->ast_type = type_node;
+            p_type->PolyStruct.name = ((AstPolyStructType *) type_node)->name;
+            p_type->PolyStruct.meta_tags = ((AstPolyStructType *) type_node)->base_struct->meta_tags;
+
+            type_register(p_type);
+            return NULL;
+        }
+
+        case Ast_Kind_Poly_Call_Type: {
+            AstPolyCallType* pc_type = (AstPolyCallType *) type_node;
+            pc_type->callee = (AstType *) strip_aliases((AstNode *) pc_type->callee);
+
+            if (!(pc_type->callee && pc_type->callee->kind == Ast_Kind_Poly_Struct_Type)) {
+                // If it is an unresolved field access or symbol, just return because an error will be printed elsewhere.
+                if (pc_type->callee->kind == Ast_Kind_Field_Access || pc_type->callee->kind == Ast_Kind_Symbol) return NULL;
+
+                onyx_report_error(pc_type->token->pos, Error_Critical, "Cannot instantiate a concrete type off of a non-polymorphic type.");
+                onyx_report_error(pc_type->callee->token->pos, Error_Critical, "Here is the type trying to be instantiated. (%s)", onyx_ast_node_kind_string(pc_type->callee->kind));
+                return NULL;
+            }
+
+            AstPolyStructType* ps_type = (AstPolyStructType *) pc_type->callee;
+
+            bh_arr(AstPolySolution) slns = NULL;
+            bh_arr_new(global_heap_allocator, slns, bh_arr_length(pc_type->params));
+            bh_arr_each(AstNode *, given, pc_type->params) {
+                if (node_is_type(*given)) {
+                    Type* param_type = type_build_from_ast(alloc, (AstType *) *given);
+
+                    // LEAK LEAK LEAK
+                    if (param_type == NULL) return NULL;
+
+                    bh_arr_push(slns, ((AstPolySolution) {
+                        .kind     = PSK_Type,
+                        .type     = param_type,
+                    }));
+                } else {
+                    bh_arr_push(slns, ((AstPolySolution) {
+                        .kind  = PSK_Value,
+                        .value = (AstTyped *) *given,
+                    }));
+                }
+            }
+
+            Type* concrete = polymorphic_struct_lookup(ps_type, slns, pc_type->token->pos, (pc_type->flags & Ast_Flag_Header_Check_No_Error) == 0);
+
+            // This should be copied in the previous function.
+            // CLEANUP: Maybe don't copy it and just use this one since it is allocated on the heap?
+            bh_arr_free(slns);
+
+            if (!concrete) return NULL;
+            if (concrete == (Type *) &node_that_signals_failure) return concrete;
+            concrete->Struct.constructed_from = (AstType *) ps_type;
+            return concrete;
+        }
+
+        case Ast_Kind_Type_Compound: {
+            AstCompoundType* ctype = (AstCompoundType *) type_node;
+
+            i64 type_count = bh_arr_length(ctype->types);
+
+            Type* comp_type = type_create(Type_Kind_Compound, alloc, type_count);
+            comp_type->Compound.size = 0;
+            comp_type->Compound.count = type_count;
+
+            fori (i, 0, type_count) {
+                assert(ctype->types[i] != NULL);
+                comp_type->Compound.types[i] = type_build_from_ast(alloc, ctype->types[i]);
+
+                // LEAK LEAK LEAK
+                if (comp_type->Compound.types[i] == NULL) return NULL;
+
+                comp_type->Compound.size += bh_max(type_size_of(comp_type->Compound.types[i]), 4);
+            }
+
+            bh_align(comp_type->Compound.size, 4);
+
+            comp_type->Compound.linear_members = NULL;
+            bh_arr_new(global_heap_allocator, comp_type->Compound.linear_members, comp_type->Compound.count);
+            build_linear_types_with_offset(comp_type, &comp_type->Compound.linear_members, 0);
+
+            type_register(comp_type);
+            return comp_type;
+        }
+
+        case Ast_Kind_Alias: {
+            AstAlias* alias = (AstAlias *) type_node;
+            return type_build_from_ast(alloc, (AstType *) alias->alias);
+        }
+
+        case Ast_Kind_Typeof: {
+            AstTypeOf* type_of = (AstTypeOf *) type_node;
+            if (type_of->resolved_type != NULL) {
+                return type_of->resolved_type;
+            }
+            
+            return NULL;
+        }
+
+        case Ast_Kind_Distinct_Type: {
+            AstDistinctType* distinct = (AstDistinctType *) type_node;
+            if (distinct->dtcache) return distinct->dtcache;
+
+            Type *base_type = type_build_from_ast(alloc, distinct->base_type);
+            if (base_type == NULL) return NULL;
+            if (base_type->kind != Type_Kind_Basic) {
+                onyx_report_error(distinct->token->pos, Error_Critical, "Distinct types can only be made out of primitive types. '%s' is not a primitive type.", type_get_name(base_type));
+                return NULL;
+            }
+
+            Type *distinct_type = type_create(Type_Kind_Distinct, alloc, 0);
+            distinct_type->Distinct.base_type = base_type;
+            distinct_type->Distinct.name = distinct->name;
+            distinct->dtcache = distinct_type;
+
+            type_register(distinct_type);
+            return distinct_type;
+        }
+    }
+
+    return NULL;
+}
+
+// CLEANUP: This needs to be merged with the very similar code from up above.
+Type* type_build_function_type(bh_allocator alloc, AstFunction* func) {
+    u64 param_count = bh_arr_length(func->params);
+
+    Type* return_type = type_build_from_ast(alloc, func->return_type);
+    if (return_type == NULL) return NULL;
+
+    Type* func_type = type_create(Type_Kind_Function, alloc, param_count);
+    func_type->Function.param_count = param_count;
+    func_type->Function.needed_param_count = 0;
+    func_type->Function.vararg_arg_pos = -1;
+    func_type->Function.return_type = return_type;
+
+    if (param_count > 0) {
+        i32 i = 0;
+        bh_arr_each(AstParam, param, func->params) {
+            if (param->default_value == NULL && param->vararg_kind == VA_Kind_Not_VA)
+                func_type->Function.needed_param_count++;
+
+            if (param->vararg_kind == VA_Kind_Untyped)
+                func_type->Function.vararg_arg_pos = i;
+
+            func_type->Function.params[i++] = param->local->type;
+        }
+    }
+
+    // CopyPaste from above in type_build_from_ast
+    char* name = (char *) type_get_unique_name(func_type);
+    if (func_type->Function.return_type != &type_auto_return) {
+        i32 index = shgeti(type_func_map, name);
+        if (index != -1) {
+            u64 id = type_func_map[index].value;
+            Type* existing_type = (Type *) bh_imap_get(&type_map, id);
+
+            // LEAK LEAK LEAK the func_type that is created
+            return existing_type;
+        }
+    }
+
+    type_register(func_type);
+    shput(type_func_map, name, func_type->id);
+
+    return func_type;
+}
+
+Type* type_build_compound_type(bh_allocator alloc, AstCompound* compound) {
+    i64 expr_count = bh_arr_length(compound->exprs);
+    fori (i, 0, expr_count) {
+        if (compound->exprs[i]->type == NULL) return NULL;
+        if (compound->exprs[i]->type->kind == Type_Kind_Basic) {
+            if (compound->exprs[i]->type->Basic.kind == Basic_Kind_Int_Unsized || compound->exprs[i]->type->Basic.kind == Basic_Kind_Float_Unsized) {
+                return NULL;
+            }
+        }
+    }
+
+    Type* comp_type = type_create(Type_Kind_Compound, alloc, expr_count);
+    comp_type->Compound.size = 0;
+    comp_type->Compound.count = expr_count;
+
+    fori (i, 0, expr_count) {
+        assert(compound->exprs[i]->type != NULL);
+        comp_type->Compound.types[i] = compound->exprs[i]->type;
+        comp_type->Compound.size += bh_max(type_size_of(comp_type->Compound.types[i]), 4);
+    }
+    
+    bh_align(comp_type->Compound.size, 4);
+    
+    comp_type->Compound.linear_members = NULL;
+    bh_arr_new(global_heap_allocator, comp_type->Compound.linear_members, comp_type->Compound.count);
+    build_linear_types_with_offset(comp_type, &comp_type->Compound.linear_members, 0);
+
+    type_register(comp_type);
+    return comp_type;
+}
+
+Type* type_build_implicit_type_of_struct_literal(bh_allocator alloc, AstStructLiteral* lit) {
+    Type* type = type_create(Type_Kind_Struct, alloc, 0);
+    type->ast_type = NULL;
+    type->Struct.name = NULL;
+    type->Struct.mem_count = bh_arr_length(lit->args.named_values);
+    type->Struct.meta_tags = NULL;
+    type->Struct.constructed_from = NULL;
+    type->Struct.status = SPS_Start;
+    type->Struct.poly_sln = NULL;
+    type_register(type);
+
+    type->Struct.memarr = NULL;
+    sh_new_arena(type->Struct.members);
+    bh_arr_new(global_heap_allocator, type->Struct.memarr, type->Struct.mem_count);
+
+    u32 size = 0;
+    u32 offset = 0;
+    u32 alignment = 1;
+    u32 idx = 0;
+    bh_arr_each(AstNamedValue *, pnv, lit->args.named_values) {
+        AstNamedValue *nv = *pnv;
+
+        Type* member_type = resolve_expression_type(nv->value);
+        if (member_type == NULL) {
+            return NULL;
+        }
+
+        u32 mem_alignment = type_alignment_of(member_type);
+        if (mem_alignment <= 0) {
+            return NULL;
+        }
+
+        alignment = bh_max(alignment, mem_alignment);
+        
+        // Should these structs be packed or not?
+        bh_align(offset, mem_alignment);
+
+        token_toggle_end(nv->token);
+        if (shgeti(type->Struct.members, nv->token->text) != -1) {
+            token_toggle_end(nv->token);
+            return NULL;
+        }
+
+        StructMember *smem = bh_alloc_item(alloc, StructMember);
+        smem->offset = offset;
+        smem->type = member_type;
+        smem->idx = idx;
+        smem->name = bh_strdup(alloc, nv->token->text);
+        smem->token = nv->token;
+        smem->initial_value = &nv->value;
+        smem->meta_tags = NULL;
+        smem->included_through_use = 0;
+        smem->used = 0;
+        smem->use_through_pointer_index = -1;
+        shput(type->Struct.members, nv->token->text, smem);
+        bh_arr_push(type->Struct.memarr, smem);
+        token_toggle_end(nv->token);
+
+        u32 type_size = type_size_of(member_type);
+        offset += type_size;
+        size = offset;
+        idx++;
+    }
+
+    type->Struct.alignment = alignment;
+    type->Struct.size = size;
+    type->Struct.linear_members = NULL;
+    bh_arr_new(global_heap_allocator, type->Struct.linear_members, type->Struct.mem_count);
+    build_linear_types_with_offset(type, &type->Struct.linear_members, 0);
+
+    type->Struct.status = SPS_Uses_Done;
+    return type;
+}
+
+Type* type_make_pointer(bh_allocator alloc, Type* to) {
+    if (to == NULL) return NULL;
+    if (to == (Type *) &node_that_signals_failure) return to;
+
+    assert(to->id > 0);
+    u64 ptr_id = bh_imap_get(&type_pointer_map, to->id);
+    if (ptr_id > 0) {
+        Type* ptr_type = (Type *) bh_imap_get(&type_map, ptr_id);
+        return ptr_type;
+
+    } else {
+        Type* ptr_type = type_create(Type_Kind_Pointer, alloc, 0);
+        ptr_type->Pointer.base.flags |= Basic_Flag_Pointer;
+        ptr_type->Pointer.base.size = POINTER_SIZE;
+        ptr_type->Pointer.elem = to;
+
+        type_register(ptr_type);
+        bh_imap_put(&type_pointer_map, to->id, ptr_type->id);
+
+        return ptr_type;
+    }
+}
+
+Type* type_make_array(bh_allocator alloc, Type* to, u32 count) {
+    if (to == NULL) return NULL;
+    if (to == (Type *) &node_that_signals_failure) return to;
+
+    assert(to->id > 0);
+    u64 key = ((((u64) to->id) << 32) | (u64) count);
+    u64 array_id = bh_imap_get(&type_array_map, key);
+    if (array_id > 0) {
+        Type* array_type = (Type *) bh_imap_get(&type_map, array_id);
+        return array_type;
+
+    } else {
+        Type* arr_type = type_create(Type_Kind_Array, alloc, 0);
+        arr_type->Array.count = count;
+        arr_type->Array.elem = to;
+        arr_type->Array.size = count * type_size_of(to);
+
+        type_register(arr_type);
+        bh_imap_put(&type_array_map, key, arr_type->id);
+
+        return arr_type;
+    }
+}
+
+Type* type_make_slice(bh_allocator alloc, Type* of) {
+    if (of == NULL) return NULL;
+    if (of == (Type *) &node_that_signals_failure) return of;
+
+    assert(of->id > 0);
+    u64 slice_id = bh_imap_get(&type_slice_map, of->id);
+    if (slice_id > 0) {
+        Type* slice_type = (Type *) bh_imap_get(&type_map, slice_id);
+        return slice_type;
+
+    } else {
+        Type* slice_type = type_create(Type_Kind_Slice, alloc, 0);
+        type_register(slice_type);
+        bh_imap_put(&type_slice_map, of->id, slice_type->id);
+
+        type_make_pointer(alloc, of);
+        slice_type->Slice.elem = of;
+
+        return slice_type;
+    }
+}
+
+Type* type_make_dynarray(bh_allocator alloc, Type* of) {
+    if (of == NULL) return NULL;
+    if (of == (Type *) &node_that_signals_failure) return of;
+
+    assert(of->id > 0);
+    u64 dynarr_id = bh_imap_get(&type_dynarr_map, of->id);
+    if (dynarr_id > 0) {
+        Type* dynarr = (Type *) bh_imap_get(&type_map, dynarr_id);
+        return dynarr;
+
+    } else {
+        Type* dynarr = type_create(Type_Kind_DynArray, alloc, 0);
+        type_register(dynarr);
+        bh_imap_put(&type_dynarr_map, of->id, dynarr->id);
+
+        type_make_pointer(alloc, of);
+        dynarr->DynArray.elem = of;
+
+        return dynarr;
+    }
+}
+
+Type* type_make_varargs(bh_allocator alloc, Type* of) {
+    if (of == NULL) return NULL;
+    if (of == (Type *) &node_that_signals_failure) return of;
+    
+    assert(of->id > 0);
+    u64 vararg_id = bh_imap_get(&type_vararg_map, of->id);
+    if (vararg_id > 0) {
+        Type* va_type = (Type *) bh_imap_get(&type_map, vararg_id);
+        return va_type;
+
+    } else {
+        Type* va_type = type_create(Type_Kind_VarArgs, alloc, 0);
+        type_register(va_type);
+        bh_imap_put(&type_vararg_map, of->id, va_type->id);
+
+        type_make_pointer(alloc, of);
+        va_type->VarArgs.elem = of;
+
+        return va_type;
+    }
+}
+
+void build_linear_types_with_offset(Type* type, bh_arr(TypeWithOffset)* pdest, u32 offset) {
+    if (type_is_structlike_strict(type)) {
+        u32 mem_count = type_structlike_mem_count(type);
+        StructMember smem = { 0 };
+        fori (i, 0, mem_count) {
+            type_lookup_member_by_idx(type, i, &smem);
+            build_linear_types_with_offset(smem.type, pdest, offset + smem.offset);
+        }
+
+    } else if (type->kind == Type_Kind_Compound) {
+        u32 elem_offset = 0;
+        fori (i, 0, type->Compound.count) {
+            build_linear_types_with_offset(type->Compound.types[i], pdest, offset + elem_offset);
+            elem_offset += bh_max(type_size_of(type->Compound.types[i]), 4);
+        }
+
+    } else {
+        bh_arr(TypeWithOffset) dest = *pdest;
+
+        TypeWithOffset two;
+        two.type = type;
+        two.offset = offset;
+        bh_arr_push(dest, two);
+
+        *pdest = dest;
+    }
+}
+
+b32 type_struct_member_apply_use(bh_allocator alloc, Type *s_type, StructMember *smem) {
+    Type* used_type = smem->type;
+
+    b32 type_is_pointer = 0;
+    if (used_type->kind == Type_Kind_Pointer) {
+        type_is_pointer = 1;
+        used_type = type_get_contained_type(used_type);
+    }
+
+    if (used_type->kind != Type_Kind_Struct) {
+        onyx_report_error(smem->token->pos, Error_Critical, "Can only use things of structure, or pointer to structure type.");
+        return 0;
+    }
+
+    if (used_type->Struct.status == SPS_Start) return 0;
+
+    bh_arr_each(StructMember*, psmem, used_type->Struct.memarr) {
+        if (shgeti(s_type->Struct.members, (*psmem)->name) != -1) {
+            onyx_report_error(smem->token->pos, Error_Critical, "Used name '%s' conflicts with existing struct member.", (*psmem)->name);
+            return 0;
+        }
+
+        StructMember* new_smem = bh_alloc_item(alloc, StructMember);
+        new_smem->type   = (*psmem)->type;
+        new_smem->name   = (*psmem)->name;
+        new_smem->meta_tags = (*psmem)->meta_tags;
+        new_smem->used = 0;
+        new_smem->included_through_use = 1;
+
+        if (type_is_pointer) {
+            new_smem->offset = (*psmem)->offset;
+            new_smem->idx = (*psmem)->idx;
+            new_smem->initial_value = NULL;
+            new_smem->use_through_pointer_index = smem->idx;
+        } else {
+            new_smem->offset = smem->offset + (*psmem)->offset;
+            new_smem->idx    = -1; // Dummy value because I don't think this is needed.
+            new_smem->initial_value = (*psmem)->initial_value;
+            new_smem->use_through_pointer_index = -1;
+        }
+
+        shput(s_type->Struct.members, (*psmem)->name, new_smem);
+    }
+
+    return 1;
+}
+
+const char* type_get_unique_name(Type* type) {
+    if (type == NULL) return "unknown";
+
+    switch (type->kind) {
+        case Type_Kind_Basic: return type->Basic.name;
+        case Type_Kind_Pointer: return bh_aprintf(global_scratch_allocator, "^%s", type_get_unique_name(type->Pointer.elem));
+        case Type_Kind_Array: return bh_aprintf(global_scratch_allocator, "[%d] %s", type->Array.count, type_get_unique_name(type->Array.elem));
+        case Type_Kind_Struct:
+            if (type->Struct.name)
+                return bh_aprintf(global_scratch_allocator, "%s@%l", type->Struct.name, type->id);
+            else
+                return bh_aprintf(global_scratch_allocator, "%s@%l", "<anonymous struct>", type->id);
+        case Type_Kind_Enum:
+            if (type->Enum.name)
+                return bh_aprintf(global_scratch_allocator, "%s@%l", type->Enum.name, type->id);
+            else
+                return bh_aprintf(global_scratch_allocator, "%s@%l", "<anonymous enum>", type->id);
+
+        case Type_Kind_Slice: return bh_aprintf(global_scratch_allocator, "[] %s", type_get_unique_name(type->Slice.elem));
+        case Type_Kind_VarArgs: return bh_aprintf(global_scratch_allocator, "..%s", type_get_unique_name(type->VarArgs.elem));
+        case Type_Kind_DynArray: return bh_aprintf(global_scratch_allocator, "[..] %s", type_get_unique_name(type->DynArray.elem));
+
+        case Type_Kind_Function: {
+            char buf[1024];
+            memset(buf, 0, 1024);
+
+            strncat(buf, "(", 1023);
+            fori (i, 0, type->Function.param_count) {
+                strncat(buf, type_get_unique_name(type->Function.params[i]), 1023);
+
+                if (i >= type->Function.needed_param_count)
+                    strncat(buf, "?", 1023);
+
+                if (i != type->Function.param_count - 1)
+                    strncat(buf, ", ", 1023);
+            }
+
+            strncat(buf, ") -> ", 1023);
+            strncat(buf, type_get_unique_name(type->Function.return_type), 1023);
+
+            return bh_aprintf(global_scratch_allocator, "%s", buf);
+        }
+
+        case Type_Kind_Compound: {
+            char buf[1024];
+            memset(buf, 0, 1024);
+
+            strncat(buf, "(", 1023);
+            fori (i, 0, type->Compound.count) {
+                strncat(buf, type_get_unique_name(type->Compound.types[i]), 1023);
+                if (i != type->Compound.count - 1)
+                    strncat(buf, ", ", 1023);
+            }
+            strncat(buf, ")", 1023);
+
+            return bh_aprintf(global_scratch_allocator, "%s", buf);
+        }
+
+        case Type_Kind_Distinct: {
+            return bh_aprintf(global_scratch_allocator, "%s@%l", type->Distinct.name, type->id);
+        }
+
+        default: return "unknown (not null)";
+    }
+}
+
+const char* type_get_name(Type* type) {
+    if (type == NULL) return "unknown";
+
+    switch (type->kind) {
+        case Type_Kind_Basic: return type->Basic.name;
+        case Type_Kind_Pointer: return bh_aprintf(global_scratch_allocator, "^%s", type_get_name(type->Pointer.elem));
+        case Type_Kind_Array: return bh_aprintf(global_scratch_allocator, "[%d] %s", type->Array.count, type_get_name(type->Array.elem));
+        case Type_Kind_Struct:
+            if (type->Struct.name)
+                return type->Struct.name;
+            else
+                return "<anonymous struct>";
+        case Type_Kind_Enum:
+            if (type->Enum.name)
+                return type->Enum.name;
+            else
+                return "<anonymous enum>";
+
+        case Type_Kind_Slice: return bh_aprintf(global_scratch_allocator, "[] %s", type_get_name(type->Slice.elem));
+        case Type_Kind_VarArgs: return bh_aprintf(global_scratch_allocator, "..%s", type_get_name(type->VarArgs.elem));
+        case Type_Kind_DynArray: return bh_aprintf(global_scratch_allocator, "[..] %s", type_get_name(type->DynArray.elem));
+
+        case Type_Kind_Function: {
+            char buf[512];
+            fori (i, 0, 512) buf[i] = 0;
+
+            strncat(buf, "(", 511);
+            fori (i, 0, type->Function.param_count) {
+                strncat(buf, type_get_name(type->Function.params[i]), 511);
+                if (i != type->Function.param_count - 1)
+                    strncat(buf, ", ", 511);
+            }
+
+            strncat(buf, ") -> ", 511);
+            strncat(buf, type_get_name(type->Function.return_type), 511);
+
+            return bh_aprintf(global_scratch_allocator, "%s", buf);
+        }
+
+        case Type_Kind_Compound: {
+            char buf[512];
+            fori (i, 0, 512) buf[i] = 0;
+
+            strncat(buf, "(", 511);
+            fori (i, 0, type->Compound.count) {
+                strncat(buf, type_get_name(type->Compound.types[i]), 511);
+                if (i != type->Compound.count - 1)
+                    strncat(buf, ", ", 511);
+            }
+            strncat(buf, ")", 511);
+
+            return bh_aprintf(global_scratch_allocator, "%s", buf);
+        }
+
+        case Type_Kind_Distinct: {
+            return bh_aprintf(global_scratch_allocator, "%s", type->Distinct.name);
+        }
+
+        default: return "unknown";
+    }
+}
+
+u32 type_get_alignment_log2(Type* type) {
+    i32 store_size = type_alignment_of(type);
+    if      (store_size == 1) return 0;
+    else if (store_size == 2) return 1;
+    else if (store_size == 4) return 2;
+    else if (store_size == 8) return 3;
+    else if (store_size == 16) return 4;
+    return 2;
+}
+
+Type* type_get_contained_type(Type* type) {
+    if (type == NULL) return NULL;
+    switch (type->kind) {
+        case Type_Kind_Pointer:  return type->Pointer.elem;
+        case Type_Kind_Array:    return type->Array.elem;
+        case Type_Kind_Slice:    return type->Slice.elem;
+        case Type_Kind_DynArray: return type->DynArray.elem;
+        case Type_Kind_VarArgs:  return type->VarArgs.elem;
+        default: return NULL;
+    }
+}
+
+static const StructMember slice_members[] = {
+    { 0,            0, NULL,                         "data",   NULL, NULL, -1, 0, 0 },
+    { POINTER_SIZE, 1, &basic_types[Basic_Kind_U32], "count",  NULL, NULL, -1, 0, 0 },
+    { POINTER_SIZE, 1, &basic_types[Basic_Kind_U32], "size",   NULL, NULL, -1, 0, 0 },
+    { POINTER_SIZE, 1, &basic_types[Basic_Kind_U32], "length", NULL, NULL, -1, 0, 0 },
+};
+
+static const StructMember array_members[] = {
+    { 0,                0, NULL,                         "data",      NULL, NULL, -1, 0, 0 },
+    { POINTER_SIZE,     1, &basic_types[Basic_Kind_U32], "count",     NULL, NULL, -1, 0, 0 },
+    { POINTER_SIZE + 4, 2, &basic_types[Basic_Kind_U32], "capacity",  NULL, NULL, -1, 0, 0 },
+    { POINTER_SIZE + 8, 3, NULL,                         "allocator", NULL, NULL, -1, 0, 0 },
+    { POINTER_SIZE,     1, &basic_types[Basic_Kind_U32], "size",      NULL, NULL, -1, 0, 0 },
+    { POINTER_SIZE,     1, &basic_types[Basic_Kind_U32], "length",    NULL, NULL, -1, 0, 0 },
+};
+
+b32 type_lookup_member(Type* type, char* member, StructMember* smem) {
+    if (type->kind == Type_Kind_Pointer) type = type->Pointer.elem;
+
+    switch (type->kind) {
+        case Type_Kind_Struct: {
+            TypeStruct* stype = &type->Struct;
+
+            i32 index = shgeti(stype->members, member);
+            if (index == -1) return 0;
+            *smem = *stype->members[index].value;
+            return 1;
+        }
+
+        case Type_Kind_VarArgs:
+        case Type_Kind_Slice: {
+            fori (i, 0, (i64) (sizeof(slice_members) / sizeof(StructMember))) {
+                if (strcmp(slice_members[i].name, member) == 0) {
+                    *smem = slice_members[i];
+                    if (smem->idx == 0) smem->type = type_make_pointer(context.ast_alloc, type->Slice.elem);
+
+                    return 1;
+                }
+            }
+            return 0;
+        }
+
+        case Type_Kind_DynArray: {
+            fori (i, 0, (i64) (sizeof(array_members) / sizeof(StructMember))) {
+                if (strcmp(array_members[i].name, member) == 0) {
+                    *smem = array_members[i];
+                    if (smem->idx == 0) smem->type = type_make_pointer(context.ast_alloc, type->DynArray.elem);
+                    if (smem->idx == 3) smem->type = type_build_from_ast(context.ast_alloc, builtin_allocator_type);
+
+                    return 1;
+                }
+            }
+            return 0;
+        }
+
+        default: return 0;
+    }
+}
+
+b32 type_lookup_member_by_idx(Type* type, i32 idx, StructMember* smem) {
+    if (type->kind == Type_Kind_Pointer) type = type->Pointer.elem;
+
+    switch (type->kind) {
+        case Type_Kind_Struct: {
+            TypeStruct* stype = &type->Struct;
+
+            if (idx > stype->mem_count) return 0;
+            *smem = *stype->memarr[idx];
+            return 1;
+        }
+
+        // HACK: This relies on the fact that the structures for Slice and VarArgs
+        // are identical.                       - brendanfh   2020/09/07
+        case Type_Kind_VarArgs:
+        case Type_Kind_Slice: {
+            if (idx > 2) return 0;
+
+            *smem = slice_members[idx];
+            if (smem->idx == 0) smem->type = type_make_pointer(context.ast_alloc, type->Slice.elem);
+
+            return 1;
+        }
+
+        case Type_Kind_DynArray: {
+            if (idx > 4) return 0;
+
+            *smem = array_members[idx];
+            if (idx == 0) smem->type = type_make_pointer(context.ast_alloc, type->DynArray.elem);
+            if (idx == 3) smem->type = type_build_from_ast(context.ast_alloc, builtin_allocator_type);
+
+            return 1;
+        }
+
+        default: return 0;
+    }
+}
+
+i32 type_linear_member_count(Type* type) {
+    switch (type->kind) {
+        case Type_Kind_Slice:
+        case Type_Kind_VarArgs:  return 2;
+        case Type_Kind_DynArray: return 5;
+        case Type_Kind_Compound: return bh_arr_length(type->Compound.linear_members);
+        case Type_Kind_Struct:   return bh_arr_length(type->Struct.linear_members);
+        default: return 1; 
+    }
+}
+
+b32 type_linear_member_lookup(Type* type, i32 idx, TypeWithOffset* two) {
+    switch (type->kind) {
+        case Type_Kind_Slice:
+        case Type_Kind_VarArgs: {
+            if (idx == 0) { 
+                two->type = type_make_pointer(context.ast_alloc, type->Slice.elem);
+                two->offset = 0;
+            }
+            if (idx == 1) {
+                two->type = &basic_types[Basic_Kind_U32];
+                two->offset = POINTER_SIZE;
+            }
+
+            return 1;
+        }
+        case Type_Kind_DynArray: {
+            if (idx == 0) { 
+                two->type = type_make_pointer(context.ast_alloc, type->DynArray.elem);
+                two->offset = 0;
+            }
+            if (idx == 1) {
+                two->type = &basic_types[Basic_Kind_U32];
+                two->offset = POINTER_SIZE;
+            }
+            if (idx == 2) {
+                two->type = &basic_types[Basic_Kind_U32];
+                two->offset = POINTER_SIZE + 4;
+            }
+            if (idx == 3 || idx == 4) {
+                Type* allocator_type = type_build_from_ast(context.ast_alloc, builtin_allocator_type);
+                type_linear_member_lookup(allocator_type, idx - 3, two);
+                two->offset += POINTER_SIZE + 8;
+            }
+
+            return 1;
+        }
+        case Type_Kind_Compound: *two = type->Compound.linear_members[idx]; return 1;
+        case Type_Kind_Struct:   *two = type->Struct.linear_members[idx];   return 1;
+
+        case Type_Kind_Distinct:
+            two->type = type->Distinct.base_type;
+            two->offset = 0;
+            return 1;
+
+        default: {
+            if (idx > 0) return 0;
+            two->offset = 0;
+            two->type = type;
+            return 1;
+        }
+    }
+}
+
+i32 type_get_idx_of_linear_member_with_offset(Type* type, u32 offset) {
+    switch (type->kind) {
+        case Type_Kind_Slice:
+        case Type_Kind_VarArgs: {
+            if (offset == 0) return 0;
+            if (offset == POINTER_SIZE) return 1;
+            return -1;
+        }
+        case Type_Kind_DynArray: {
+            if (offset == 0)                    return 0;
+            if (offset == POINTER_SIZE)         return 1;
+            if (offset == POINTER_SIZE + 4)     return 2;
+            if (offset == POINTER_SIZE + 8)     return 3;
+            if (offset == POINTER_SIZE * 2 + 8) return 4;
+            return -1;
+        }
+        case Type_Kind_Compound: {
+            i32 idx = 0;
+            bh_arr_each(TypeWithOffset, two, type->Compound.linear_members) {
+                if (two->offset == offset) return idx;
+                idx++;
+            }
+
+            return -1;
+        }
+        case Type_Kind_Struct: {
+            i32 idx = 0;
+            bh_arr_each(TypeWithOffset, two, type->Struct.linear_members) {
+                if (two->offset == offset) return idx;
+                idx++;
+            }
+
+            return -1;
+        }
+        default: return -1;
+    }
+}
+
+b32 type_struct_is_simple(Type* type) {
+    if (type->kind != Type_Kind_Struct) return 0;
+
+    b32 is_simple = 1;
+    bh_arr_each(StructMember *, mem, type->Struct.memarr) {
+        if (type_is_compound((*mem)->type) || (*mem)->type->kind == Type_Kind_Array) {
+            is_simple = 0;
+            break;
+        }
+    }
+
+    return is_simple;
+}
+
+b32 type_is_pointer(Type* type) {
+    if (type == NULL) return 0;
+    return type->kind == Type_Kind_Pointer;
+}
+
+b32 type_is_rawptr(Type* type) {
+    if (type == NULL) return 0;
+    return type->kind == Type_Kind_Basic && type->Basic.kind == Basic_Kind_Rawptr;
+}
+
+b32 type_is_array(Type* type) {
+    if (type == NULL) return 0;
+    return type->kind == Type_Kind_Array;
+}
+
+b32 type_is_struct(Type* type) {
+    if (type == NULL) return 0;
+    if (type->kind == Type_Kind_Struct) return 1;
+    if (type->kind == Type_Kind_Pointer)
+        if (type->Pointer.elem != NULL && type->Pointer.elem->kind == Type_Kind_Struct) return 1;
+    return 0;
+}
+
+b32 type_is_bool(Type* type) {
+    if (type == NULL) return 0;
+    return type != NULL && type->kind == Type_Kind_Basic && type->Basic.kind == Basic_Kind_Bool;
+}
+
+b32 type_is_small_integer(Type* type) {
+    if (type == NULL) return 0;
+    if (type->kind == Type_Kind_Enum) return type_is_small_integer(type->Enum.backing);
+    if (type->kind == Type_Kind_Distinct) return type_is_small_integer(type->Distinct.base_type);
+    if (type->kind != Type_Kind_Basic) return 0;
+
+    return type->Basic.kind >= Basic_Kind_I8 && type->Basic.kind <= Basic_Kind_U32;
+}
+
+b32 type_is_integer(Type* type) {
+    if (type == NULL) return 0;
+    if (type->kind == Type_Kind_Enum) return type_is_integer(type->Enum.backing);
+    if (type->kind == Type_Kind_Distinct) return type_is_integer(type->Distinct.base_type);
+    if (type->kind != Type_Kind_Basic) return 0;
+
+    return (type->Basic.kind >= Basic_Kind_I8 && type->Basic.kind <= Basic_Kind_U64)
+        || type->Basic.kind == Basic_Kind_Type_Index;
+}
+
+b32 type_is_numeric(Type* type) {
+    if (type == NULL) return 0;
+    if (type->kind == Type_Kind_Enum) return 1;
+    if (type->kind == Type_Kind_Distinct) return type_is_numeric(type->Distinct.base_type);
+    if (type->kind != Type_Kind_Basic) return 0;
+
+    return type->Basic.kind >= Basic_Kind_Int_Unsized && type->Basic.kind <= Basic_Kind_F64;
+}
+
+b32 type_is_compound(Type* type) {
+    if (type == NULL) return 0;
+
+    if (type->kind == Type_Kind_Struct) {
+        //
+        // This is for the kind of common case where a structure simply wraps a
+        // single non-compound value; in this situation, the structure can be
+        // "dissolved" at compile-time and turn into the underlying type.
+        //
+        
+        if (bh_arr_length(type->Struct.linear_members) != 1) return 1;
+        return type_is_compound(type->Struct.linear_members[0].type);
+    }
+
+    return type->kind != Type_Kind_Basic
+        && type->kind != Type_Kind_Pointer
+        && type->kind != Type_Kind_Enum
+        && type->kind != Type_Kind_Function
+        && type->kind != Type_Kind_Array
+        && type->kind != Type_Kind_Distinct;
+}
+
+b32 type_is_simd(Type* type) {
+    if (type == NULL) return 0;
+    if (type->kind != Type_Kind_Basic) return 0;
+    return type->Basic.flags & Basic_Flag_SIMD;
+}
+
+b32 type_results_in_void(Type* type) {
+    return (type == NULL)
+        || (type->kind == Type_Kind_Basic && type->Basic.kind == Basic_Kind_Void)
+        || (   (type->kind == Type_Kind_Function)
+            && (type->Function.return_type->kind == Type_Kind_Basic)
+            && (type->Function.return_type->Basic.kind == Basic_Kind_Void));
+}
+
+b32 type_is_array_accessible(Type* type) {
+    if (type == NULL) return 0;
+    if (type_is_pointer(type)) return 1;
+    if (type->kind == Type_Kind_Array) return 1;
+    if (type->kind == Type_Kind_Slice) return 1;
+    if (type->kind == Type_Kind_DynArray) return 1;
+    if (type->kind == Type_Kind_VarArgs) return 1;
+    return 0;
+}
+
+b32 type_is_structlike(Type* type) {
+    if (type == NULL) return 0;
+    if (type->kind == Type_Kind_Array) return 1;
+    if (type->kind == Type_Kind_Struct) return 1;
+    if (type->kind == Type_Kind_Slice)  return 1;
+    if (type->kind == Type_Kind_DynArray) return 1;
+    if (type->kind == Type_Kind_VarArgs) return 1;
+    if (type->kind == Type_Kind_Pointer) {
+        if (type->Pointer.elem->kind == Type_Kind_Struct) return 1;
+        if (type->Pointer.elem->kind == Type_Kind_Slice)  return 1;
+        if (type->Pointer.elem->kind == Type_Kind_DynArray) return 1;
+    }
+    return 0;
+}
+
+b32 type_is_structlike_strict(Type* type) {
+    if (type == NULL) return 0;
+    if (type->kind == Type_Kind_Struct)   return 1;
+    if (type->kind == Type_Kind_Slice)    return 1;
+    if (type->kind == Type_Kind_DynArray) return 1;
+    if (type->kind == Type_Kind_VarArgs)  return 1;
+    return 0;
+}
+
+u32 type_structlike_mem_count(Type* type) {
+    if (type == NULL) return 0;
+    switch (type->kind) {
+        case Type_Kind_Struct:   return type->Struct.mem_count;
+        case Type_Kind_Slice:    return 2;
+        case Type_Kind_VarArgs:  return 2;
+        case Type_Kind_DynArray: return 4;
+        default: return 0;
+    }
+}
+
+u32 type_structlike_is_simple(Type* type) {
+    if (type == NULL) return 0;
+    switch (type->kind) {
+        case Type_Kind_Struct:   return type_struct_is_simple(type);
+        case Type_Kind_Slice:    return 1;
+        case Type_Kind_VarArgs:  return 1;
+        case Type_Kind_DynArray: return 0;
+        default: return 0;
+    }
+}
+
+b32 type_is_sl_constructable(Type* type) {
+    if (type == NULL) return 0;
+    switch (type->kind) {
+        case Type_Kind_Struct:   return 1;
+        case Type_Kind_Slice:    return 1;
+        case Type_Kind_DynArray: return 1;
+        default: return 0;
+    }
+}
+
+b32 type_struct_constructed_from_poly_struct(Type* struct_type, struct AstType* from) {
+    if (struct_type == NULL) return 0;
+    if (struct_type->kind != Type_Kind_Struct) return 0;
+
+    return struct_type->Struct.constructed_from == from;
+}
diff --git a/compiler/src/utils.c b/compiler/src/utils.c
new file mode 100644 (file)
index 0000000..7f4ea49
--- /dev/null
@@ -0,0 +1,1206 @@
+#define BH_DEBUG
+
+#include "utils.h"
+#include "lex.h"
+#include "astnodes.h"
+#include "errors.h"
+#include "parser.h"
+#include "astnodes.h"
+#include "errors.h"
+
+bh_scratch global_scratch;
+bh_allocator global_scratch_allocator;
+
+bh_managed_heap global_heap;
+bh_allocator global_heap_allocator;
+
+//
+// Program info and packages
+//
+Package* package_lookup(char* package_name) {
+    i32 index = shgeti(context.packages, package_name);
+    if (index != -1) {
+        return context.packages[index].value;
+    } else {
+        return NULL;
+    }
+}
+
+Package* package_lookup_or_create(char* package_name, Scope* parent_scope, bh_allocator alloc, OnyxFilePos pos) {
+    i32 index = shgeti(context.packages, package_name);
+    if (index != -1) {
+        return context.packages[index].value;
+
+    } else {
+        Package* package = bh_alloc_item(alloc, Package);
+
+        char* pac_name = bh_alloc_array(alloc, char, strlen(package_name) + 1);
+        memcpy(pac_name, package_name, strlen(package_name) + 1);
+        pac_name[strlen(package_name)] = '\0';
+
+        package->name = pac_name;
+        package->use_package_entities = NULL;
+
+        if (!strcmp(pac_name, "builtin")) {
+            package->private_scope = scope_create(alloc, context.global_scope, pos);
+            package->scope = context.global_scope;
+        } else {
+            package->scope = scope_create(alloc, parent_scope, pos);
+            package->private_scope = scope_create(alloc, package->scope, pos);
+        }
+
+        shput(context.packages, pac_name, package);
+
+        if (!charset_contains(pac_name, '.')) {
+            AstPackage* package_node = onyx_ast_node_new(alloc, sizeof(AstPackage), Ast_Kind_Package);
+            package_node->package_name = package->name;
+            package_node->package = package;
+
+            symbol_raw_introduce(context.global_scope, pac_name, pos, (AstNode *) package_node);
+        }
+
+        return package;
+    }
+}
+
+void package_track_use_package(Package* package, Entity* entity) {
+    assert(entity);
+
+    if (package->use_package_entities == NULL) {
+        bh_arr_new(global_heap_allocator, package->use_package_entities, 4);
+    }
+
+    bh_arr_push(package->use_package_entities, entity);
+}
+
+void package_reinsert_use_packages(Package* package) {
+    if (!package) return;
+    if (!package->use_package_entities) return;
+
+    bh_arr_each(Entity *, use_package, package->use_package_entities) {
+        (*use_package)->state = Entity_State_Resolve_Symbols;
+        (*use_package)->macro_attempts = 0;
+        entity_heap_insert_existing(&context.entities, *use_package);
+    } 
+
+    bh_arr_set_length(package->use_package_entities, 0);
+}
+
+
+//
+// Scoping
+//
+static u64 next_scope_id = 1;
+
+Scope* scope_create(bh_allocator a, Scope* parent, OnyxFilePos created_at) {
+    Scope* scope = bh_alloc_item(a, Scope);
+    scope->id = next_scope_id++;
+    scope->parent = parent;
+    scope->created_at = created_at;
+    scope->name = NULL;
+
+    scope->symbols = NULL;
+    sh_new_arena(scope->symbols);
+
+    return scope;
+}
+
+void scope_include(Scope* target, Scope* source, OnyxFilePos pos) {
+    fori (i, 0, shlen(source->symbols)) {
+        symbol_raw_introduce(target, source->symbols[i].key, pos, source->symbols[i].value);
+    }
+}
+
+b32 symbol_introduce(Scope* scope, OnyxToken* tkn, AstNode* symbol) {
+    token_toggle_end(tkn);
+
+    b32 ret = symbol_raw_introduce(scope, tkn->text, tkn->pos, symbol);
+
+    token_toggle_end(tkn);
+    return ret;
+}
+
+b32 symbol_raw_introduce(Scope* scope, char* name, OnyxFilePos pos, AstNode* symbol) {
+    if (strcmp(name, "_")) {
+        i32 index = shgeti(scope->symbols, name);
+        if (index != -1) {
+            AstNode *node = scope->symbols[index].value;
+            if (node != symbol) {
+                onyx_report_error(pos, Error_Critical, "Redeclaration of symbol '%s'.", name);
+
+                if (node->token) {
+                    onyx_report_error(node->token->pos, Error_Critical, "Previous declaration was here.");
+                }
+
+                return 0;
+            }
+            return 1;
+        }
+    }
+
+    shput(scope->symbols, name, symbol);
+    return 1;
+}
+
+void symbol_builtin_introduce(Scope* scope, char* sym, AstNode *node) {
+    shput(scope->symbols, sym, node);
+}
+
+void symbol_subpackage_introduce(Scope* scope, char* sym, AstPackage* package) {
+    i32 index = shgeti(scope->symbols, sym);
+    if (index != -1) {
+        AstNode* maybe_package = scope->symbols[index].value;
+        
+        // CLEANUP: Make this assertion an actual error message.
+        assert(maybe_package->kind == Ast_Kind_Package);
+    } else {
+        shput(scope->symbols, sym, (AstNode *) package);
+    }
+}
+
+AstNode* symbol_raw_resolve(Scope* start_scope, char* sym) {
+    Scope* scope = start_scope;
+
+    while (scope != NULL) {
+        i32 index = shgeti(scope->symbols, sym);
+        if (index != -1) {
+            AstNode* res = scope->symbols[index].value;
+
+            if ((res->flags & Ast_Flag_Symbol_Invisible) == 0) {
+                return res;
+            }
+        }
+
+        scope = scope->parent;
+    }
+
+    return NULL;
+}
+
+AstNode* symbol_resolve(Scope* start_scope, OnyxToken* tkn) {
+    token_toggle_end(tkn);
+    AstNode* res = symbol_raw_resolve(start_scope, tkn->text);
+    token_toggle_end(tkn);
+
+    return res;
+}
+
+AstNode* try_symbol_raw_resolve_from_node(AstNode* node, char* symbol) {
+    b32 used_pointer = 0;
+
+    while (1) {
+        if (!node) return NULL;
+
+        switch (node->kind) {
+            case Ast_Kind_Type_Raw_Alias: node = (AstNode *) ((AstTypeRawAlias *) node)->to->ast_type; break;
+            case Ast_Kind_Type_Alias:     node = (AstNode *) ((AstTypeAlias *) node)->to; break;
+            case Ast_Kind_Alias:          node = (AstNode *) ((AstAlias *) node)->alias; break;
+            case Ast_Kind_Pointer_Type: {
+                if (used_pointer) goto all_types_peeled_off;
+                used_pointer = 1;
+
+                node = (AstNode *) ((AstPointerType *) node)->elem;
+                break;
+            }
+
+            default: goto all_types_peeled_off;
+        }
+    }
+
+all_types_peeled_off:
+    if (!node) return NULL;
+
+    switch (node->kind) {
+        case Ast_Kind_Package: {
+            AstPackage* package = (AstPackage *) node;
+
+            // CLEANUP
+            if (package->package == NULL) {
+                package->package = package_lookup(package->package_name);
+            }
+
+            if (package->package == NULL) {
+                return NULL;
+            }
+
+            return symbol_raw_resolve(package->package->scope, symbol);
+        } 
+
+        case Ast_Kind_Foreign_Block: {
+            AstForeignBlock* fb = (AstForeignBlock *) node;
+
+            if (fb->scope == NULL)
+                return NULL;
+
+            return symbol_raw_resolve(fb->scope, symbol);
+        }
+
+        case Ast_Kind_Enum_Type: {
+            AstEnumType* etype = (AstEnumType *) node;
+            return symbol_raw_resolve(etype->scope, symbol);
+        }
+
+        case Ast_Kind_Struct_Type: {
+            AstStructType* stype = (AstStructType *) node;
+
+            // HACK HACK
+            // Temporarily disable the parent scope so that you can't access things
+            // "above" the structures scope. This leads to unintended behavior, as when
+            // you are accessing a static element on a structure, you don't expect to
+            // bleed to the top level scope.
+            AstNode *result = NULL;
+            if (stype->scope) {
+                Scope *tmp_parent = stype->scope->parent;
+                stype->scope->parent = NULL;
+                result = symbol_raw_resolve(stype->scope, symbol);
+                stype->scope->parent = tmp_parent;
+            }
+
+            if (result == NULL && stype->stcache != NULL) {
+                Type* struct_type = stype->stcache;
+                assert(struct_type->kind == Type_Kind_Struct);
+
+                bh_arr_each(AstPolySolution, sln, struct_type->Struct.poly_sln) {
+                    if (token_text_equals(sln->poly_sym->token, symbol)) {
+                        if (sln->kind == PSK_Type) {
+                            result = (AstNode *) sln->type->ast_type;
+                        } else {
+                            result = (AstNode *) sln->value;
+                        }
+                    }
+                }
+            }
+
+            return result;
+        }
+
+        case Ast_Kind_Poly_Struct_Type: {
+            AstStructType* stype = ((AstPolyStructType *) node)->base_struct;
+            return symbol_raw_resolve(stype->scope, symbol);
+        }
+
+        case Ast_Kind_Poly_Call_Type: {
+            AstNode* callee = (AstNode *) ((AstPolyCallType *) node)->callee;
+            return try_symbol_raw_resolve_from_node(callee, symbol);
+        }
+    }
+
+    return NULL;
+}
+
+AstNode* try_symbol_resolve_from_node(AstNode* node, OnyxToken* token) {
+    token_toggle_end(token);
+    AstNode* result = try_symbol_raw_resolve_from_node(node, token->text);
+    token_toggle_end(token);
+
+    return result;
+}
+
+AstNode* try_symbol_raw_resolve_from_type(Type *type, char* symbol) {
+    while (type->kind == Type_Kind_Pointer) {
+        type = type->Pointer.elem; 
+    }
+
+    if (type->kind == Type_Kind_Struct) {
+        if (type->Struct.poly_sln == NULL) return NULL;
+
+        bh_arr_each(AstPolySolution, sln, type->Struct.poly_sln) {
+            if (token_text_equals(sln->poly_sym->token, symbol)) {
+                if (sln->kind == PSK_Type) {
+                    AstTypeRawAlias* alias = onyx_ast_node_new(context.ast_alloc, sizeof(AstTypeRawAlias), Ast_Kind_Type_Raw_Alias);
+                    alias->type = &basic_types[Basic_Kind_Type_Index];
+                    alias->to = sln->type;
+                    return (AstNode *) alias;
+
+                } else {
+                    return (AstNode *) sln->value;
+                }
+            }
+        }
+    }
+
+    return NULL;
+}
+
+void scope_clear(Scope* scope) {
+    sh_new_arena(scope->symbols);
+}
+
+// Polymorphic procedures are in their own file to clean up this file.
+#include "polymorph.h"
+
+//
+// Overloaded Procedures
+//
+
+//
+// @Cleanup: Everything having to do with overload resolving!
+// Things that need to be available:
+//  * A copy of the arguments list that can be mutated
+//      - The named_values do not need to be copied, because they are not modified when fill_in_arguments happens.
+//      - Only values needs to have a copy available
+//      - This copy needs to be reset after checking every option
+//
+// Steps needed to check if an overload option is "the one":
+//  1. Figure out what the overload is
+//      a. If polymorphic, generate the header for the procedure only
+//  2. Place the arguments in the copy, according to the overload option
+//  3. Ensure the option has a type filled out
+//  4. For each argument
+//      a. Ensure it has a place to go (not too many arguments)
+//      b. Ensure it has a type
+//      c. Ensure the types match (currently there could be a problem if an option is attempted and doesn't work all the way that polymorphic procedures as arguments could still be solidified)
+//
+// Additional features that this needs to account for:
+//  * Resolving an overload from a list of parameter types
+//  * Resolving an overload from a TypeFunction (so an overloaded procedure can be passed as a parameter)
+//
+
+void add_overload_option(bh_arr(OverloadOption)* poverloads, u64 precedence, AstTyped* overload) {
+    bh_arr(OverloadOption) overloads = *poverloads;
+
+    i32 index = -1;
+    fori (i, 0, bh_arr_length(overloads)) {
+        if (overloads[i].precedence > precedence) {
+            index = i;
+            break;
+        }
+    }
+
+    if (index < 0) {
+        bh_arr_push(overloads, ((OverloadOption) {
+            .precedence = precedence,
+            .option     = overload,
+        }));
+
+    } else {
+        bh_arr_insertn(overloads, index, 1);
+        overloads[index].precedence = precedence;
+        overloads[index].option     = overload;
+    }
+
+    *poverloads = overloads;
+}
+
+// NOTE: The job of this function is to take a set of overloads, and traverse it to add all possible
+// overloads that are reachable. This is slightly more difficult than it may seem. In this language,
+// overloaded procedures have a strict ordering to their overloads, which determines how the correct
+// match will be found. This was not very complicated until overloaded procedures could be used as
+// overload options. This means that you could create an "infinite loop" of overloads like so:
+// 
+//  o1 :: {                 o2 :: {
+//      (...) { ... },          (...) { ... },
+//      o2                      o1
+//  }                       }
+//
+// Obviously, this is not really an infinite loop. It just means that all options are available if
+// o1 or o2 are called. The difference between o1 and o2 is the order that the overloads will be
+// searched. To build the the list of overloads, a hashmap is used to prevent the problem from being
+// O(n^2), even though n would (probably) be small. bh_imap has the useful property that it maintains
+// an "entries" array that, so long as nothing is ever removed from it, will maintain the order in
+// which entries were put into the map. This is useful because a simple recursive algorithm can
+// collect all the overloads into the map, and also use the map to provide a base case.
+void build_all_overload_options(bh_arr(OverloadOption) overloads, bh_imap* all_overloads) {
+    bh_arr_each(OverloadOption, overload, overloads) {
+        if (bh_imap_has(all_overloads, (u64) overload->option)) continue;
+
+        bh_imap_put(all_overloads, (u64) overload->option, 1);
+
+        if (overload->option->kind == Ast_Kind_Overloaded_Function) {
+            AstOverloadedFunction* sub_overload = (AstOverloadedFunction *) overload->option;
+            build_all_overload_options(sub_overload->overloads, all_overloads);
+        }
+    }
+}
+
+AstTyped* find_matching_overload_by_arguments(bh_arr(OverloadOption) overloads, Arguments* param_args) {
+    Arguments args;
+    arguments_clone(&args, param_args);
+    arguments_ensure_length(&args, bh_arr_length(args.values) + bh_arr_length(args.named_values));
+
+    // CLEANUP SPEED: This currently rebuilds the complete set of overloads every time one is looked up.
+    // This should be cached in the AstOverloadedFunction or somewhere like that.
+    bh_imap all_overloads;
+    bh_imap_init(&all_overloads, global_heap_allocator, bh_arr_length(overloads) * 2);
+    build_all_overload_options(overloads, &all_overloads);
+
+    AstTyped *matched_overload = NULL;
+
+    bh_arr_each(bh__imap_entry, entry, all_overloads.entries) {
+        AstTyped* node = (AstTyped *) strip_aliases((AstNode *) entry->key);
+        arguments_copy(&args, param_args);
+
+        AstFunction* overload = NULL;
+        switch (node->kind) {
+            case Ast_Kind_Macro:            overload = macro_resolve_header((AstMacro *) node, param_args, NULL, 0); break;
+            case Ast_Kind_Polymorphic_Proc: overload = polymorphic_proc_build_only_header((AstFunction *) node, PPLM_By_Arguments, param_args); break;
+            case Ast_Kind_Function:
+                overload = (AstFunction *) node;
+                arguments_clear_baked_flags(&args);
+                break;
+        }
+
+        // NOTE: Overload is not something that is known to be overloadable.
+        if (overload == NULL) continue;
+        if (overload == (AstFunction *) &node_that_signals_a_yield) return (AstTyped *) overload;
+        if (overload->kind != Ast_Kind_Function) continue;
+        if (overload->type == NULL) {
+            // If it was not possible to create the type for this procedure, tell the
+            // caller that this should yield and try again later.
+
+            // return and not continue because if the overload that didn't have a type will
+            // work in the future, then it has to take precedence over the other options available.
+            return (AstTyped *) &node_that_signals_a_yield;
+        }
+        assert(overload->type->kind == Type_Kind_Function);
+
+        arguments_remove_baked(&args);
+        arguments_ensure_length(&args, get_argument_buffer_size(&overload->type->Function, &args));
+
+        // NOTE: If the arguments cannot be placed successfully in the parameters list
+        if (!fill_in_arguments(&args, (AstNode *) overload, NULL, 0)) continue;
+        
+        VarArgKind va_kind;
+        TypeMatch tm = check_arguments_against_type(&args, &overload->type->Function, &va_kind, NULL, NULL, NULL);
+        if (tm == TYPE_MATCH_SUCCESS) {
+            matched_overload = node;
+            break;
+        }
+
+        if (tm == TYPE_MATCH_YIELD) {
+            return (AstTyped *) &node_that_signals_a_yield;
+        }
+    }
+
+    bh_imap_free(&all_overloads);
+    bh_arr_free(args.values);
+    return matched_overload;
+}
+
+AstTyped* find_matching_overload_by_type(bh_arr(OverloadOption) overloads, Type* type) {
+    if (type->kind != Type_Kind_Function) return NULL;
+
+    bh_imap all_overloads;
+    bh_imap_init(&all_overloads, global_heap_allocator, bh_arr_length(overloads) * 2);
+    build_all_overload_options(overloads, &all_overloads);
+
+    AstTyped *matched_overload = NULL;
+
+    bh_arr_each(bh__imap_entry, entry, all_overloads.entries) {
+        AstTyped* node = (AstTyped *) entry->key;
+        if (node->kind == Ast_Kind_Overloaded_Function) continue;
+
+        TypeMatch tm = unify_node_and_type(&node, type);
+        if (tm == TYPE_MATCH_SUCCESS) {
+            matched_overload = node;
+            break;
+        }
+
+        if (tm == TYPE_MATCH_YIELD) {
+            return (AstTyped *) &node_that_signals_a_yield;
+        }
+    }
+    
+    bh_imap_free(&all_overloads);
+    return matched_overload;
+}
+
+void report_unable_to_match_overload(AstCall* call, bh_arr(OverloadOption) overloads) {
+    char* arg_str = bh_alloc(global_scratch_allocator, 1024);
+    arg_str[0] = '\0';
+
+    bh_arr_each(AstTyped *, arg, call->args.values) {
+        strncat(arg_str, node_get_type_name(*arg), 1023);
+
+        if (arg != &bh_arr_last(call->args.values))
+            strncat(arg_str, ", ", 1023);
+    }
+
+    if (bh_arr_length(call->args.named_values) > 0) {
+        if (bh_arr_length(call->args.values) > 0) {
+            strncat(arg_str, ", ", 1023);
+        }
+
+        bh_arr_each(AstNamedValue *, named_value, call->args.named_values) { 
+            token_toggle_end((*named_value)->token);
+            strncat(arg_str, (*named_value)->token->text, 1023);
+            token_toggle_end((*named_value)->token);
+
+            strncat(arg_str, "=", 1023);
+            strncat(arg_str, node_get_type_name((*named_value)->value), 1023); // CHECK: this might say 'unknown'.
+
+            if (named_value != &bh_arr_last(call->args.named_values))
+                strncat(arg_str, ", ", 1023);
+        }
+    }
+
+    onyx_report_error(call->token->pos, Error_Critical, "Unable to match overloaded function with provided argument types: (%s)", arg_str);
+
+    bh_free(global_scratch_allocator, arg_str);
+
+    // CLEANUP SPEED: This currently rebuilds the complete set of overloads every time one is looked up.
+    // This should be cached in the AstOverloadedFunction or somewhere like that.
+    bh_imap all_overloads;
+    bh_imap_init(&all_overloads, global_heap_allocator, bh_arr_length(overloads) * 2);
+    build_all_overload_options(overloads, &all_overloads);
+
+    i32 i = 1;
+    bh_arr_each(bh__imap_entry, entry, all_overloads.entries) {
+        AstTyped* node = (AstTyped *) strip_aliases((AstNode *) entry->key);
+        onyx_report_error(node->token->pos, Error_Critical, "Here is one of the overloads. %d/%d", i++, bh_arr_length(all_overloads.entries));
+    }
+
+    bh_imap_free(&all_overloads);
+}
+
+
+//
+// Macros
+//
+//
+// TODO: Write this documentation
+void expand_macro(AstCall** pcall, AstFunction* template) {
+    AstCall* call = *pcall;
+    AstMacro* macro = (AstMacro *) call->callee;
+    assert(macro->kind == Ast_Kind_Macro);
+
+    assert(template->kind == Ast_Kind_Function);
+    assert(template->type != NULL);
+    assert(template->type->kind == Type_Kind_Function);
+
+    AstBlock* expansion = (AstBlock *) ast_clone(context.ast_alloc, template->body);
+    expansion->rules = Block_Rule_Macro;
+    expansion->scope = NULL;
+    expansion->next = call->next;
+
+    AstNode* subst = (AstNode *) expansion;
+
+    if (template->type->Function.return_type != &basic_types[Basic_Kind_Void]) {
+        AstDoBlock* doblock = (AstDoBlock *) onyx_ast_node_new(context.ast_alloc, sizeof(AstDoBlock), Ast_Kind_Do_Block);
+        doblock->token = expansion->token;
+        doblock->block = expansion;
+        doblock->type = template->type->Function.return_type;
+        doblock->next = expansion->next;
+        expansion->next = NULL;
+
+        subst = (AstNode *) doblock;
+    }
+
+    Scope* argument_scope = scope_create(context.ast_alloc, NULL, call->token->pos);
+    if (expansion->binding_scope != NULL)
+        scope_include(argument_scope, expansion->binding_scope, call->token->pos);
+    expansion->binding_scope = argument_scope;
+
+    // HACK HACK HACK This is probably very wrong. I don't know what guarentees that
+    // the paramters and arguments are going to be in the same order exactly.
+    Type *any_type = type_build_from_ast(context.ast_alloc, builtin_any_type);
+    fori (i, 0, bh_arr_length(call->args.values)) {
+        AstNode *value = (AstNode *) ((AstArgument *) call->args.values[i])->value;
+        assert(template->params[i].local->type);
+
+        Type *param_type = template->params[i].local->type;
+        if (param_type == any_type
+            || (param_type->kind == Type_Kind_VarArgs && param_type->VarArgs.elem == any_type)) {
+            onyx_report_error(macro->token->pos, Error_Critical, "Currently, macros do not support arguments of type 'any' or '..any'.");
+        }
+
+        symbol_introduce(argument_scope, template->params[i].local->token, value);
+    }
+
+    if (template->poly_scope != NULL)
+        scope_include(argument_scope, template->poly_scope, call->token->pos);
+
+    *(AstNode **) pcall = subst;
+    return;
+}
+
+AstFunction* macro_resolve_header(AstMacro* macro, Arguments* args, OnyxToken* callsite, b32 error_if_failed) {
+    switch (macro->body->kind) {
+        case Ast_Kind_Function: return (AstFunction *) macro->body;
+
+        case Ast_Kind_Polymorphic_Proc: {
+            AstFunction* pp = (AstFunction *) macro->body;
+            ensure_polyproc_cache_is_created(pp);
+
+            bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, PPLM_By_Arguments, args, callsite, error_if_failed);
+
+            if (slns == NULL) {
+                if (flag_to_yield) {
+                    flag_to_yield = 0;
+                    return (AstFunction *) &node_that_signals_a_yield;
+                }
+
+                return NULL;
+            }
+
+            return polymorphic_proc_build_only_header_with_slns(pp, slns, error_if_failed);
+        }
+
+        default: assert(("Bad macro body type.", 0));
+    }
+
+    return NULL;
+}
+
+
+//
+// Arguments resolving
+//
+static i32 lookup_idx_by_name(AstNode* provider, char* name) {
+    switch (provider->kind) {
+        case Ast_Kind_Struct_Literal: {
+            AstStructLiteral* sl = (AstStructLiteral *) provider;
+            assert(sl->type);
+
+            StructMember s;
+            if (!type_lookup_member(sl->type, name, &s)) return -1;
+            if (s.included_through_use) return -1;
+
+            return s.idx;
+        }
+
+        case Ast_Kind_Function: {
+            AstFunction* func = (AstFunction *) provider;
+
+            i32 param_idx = -1;
+            i32 idx = 0;
+            bh_arr_each(AstParam, param, func->params) {
+                if (token_text_equals(param->local->token, name)) {
+                    param_idx = idx;
+                    break;
+                }
+
+                idx++;
+            }
+
+            return param_idx;
+        }
+
+        default: return -1;
+    }
+}
+
+static AstNode* lookup_default_value_by_idx(AstNode* provider, i32 idx) {
+    switch (provider->kind) {
+        case Ast_Kind_Struct_Literal: {
+            AstStructLiteral* sl = (AstStructLiteral *) provider;
+            assert(sl->type);
+
+            if (sl->type->kind == Type_Kind_Struct) {
+                bh_arr(StructMember *) memarr = sl->type->Struct.memarr;
+                if (idx >= bh_arr_length(memarr)) return NULL;
+
+                return (AstNode *) *memarr[idx]->initial_value;
+            }
+
+            return NULL;
+        }
+
+        case Ast_Kind_Function: {
+            AstFunction* func = (AstFunction *) provider;
+
+            AstTyped* default_value = func->params[idx].default_value;
+            if (default_value == NULL) return NULL;
+
+            AstArgument* arg = make_argument(context.ast_alloc, default_value);
+            return (AstNode *) arg;
+        }
+
+        default: return NULL;
+    }
+}
+
+static i32 maximum_argument_count(AstNode* provider) {
+    switch (provider->kind) {
+        case Ast_Kind_Struct_Literal: {
+            AstStructLiteral* sl = (AstStructLiteral *) provider;
+            assert(sl->type);
+
+            return type_structlike_mem_count(sl->type);
+        }
+    }
+
+    // NOTE: This returns int_max for anything other than struct literals because the
+    // bounds checking on the arguments will be done elsewhere.
+    return 0x7fffffff;
+}
+
+static i32 non_baked_argument_count(Arguments* args) {
+    if (args->used_argument_count >= 0) return args->used_argument_count;
+
+    i32 count = 0;
+
+    bh_arr_each(AstTyped *, actual, args->values) {
+        if ((*actual)->kind != Ast_Kind_Argument) count++;
+        else if (!((AstArgument *) (*actual))->is_baked) count++;
+    }
+
+    bh_arr_each(AstNamedValue *, named_value, args->named_values) {
+        if ((*named_value)->value->kind != Ast_Kind_Argument) count++;
+        else if (!((AstArgument *) (*named_value)->value)->is_baked) count++;
+    }
+
+    args->used_argument_count = count;
+    return count;
+}
+
+i32 get_argument_buffer_size(TypeFunction* type, Arguments* args) {
+    i32 non_vararg_param_count = (i32) type->param_count;
+    if (non_vararg_param_count > 0) {
+        if (type->params[type->param_count - 1] == builtin_vararg_type_type) non_vararg_param_count--;
+        if (type->params[type->param_count - 1]->kind == Type_Kind_VarArgs)  non_vararg_param_count--;
+    }
+
+    return bh_max(non_vararg_param_count, non_baked_argument_count(args));
+}
+
+// NOTE: The values array can be partially filled out, and is the resulting array.
+// Returns if all the values were filled in.
+b32 fill_in_arguments(Arguments* args, AstNode* provider, char** err_msg, b32 insert_zero_values) {
+
+    { // Delete baked arguments
+        // :ArgumentResolvingIsComplicated
+        i32 max = bh_arr_length(args->values);
+        fori (i, 0, max) {
+            AstTyped* value = args->values[i];
+            if (value == NULL) continue;
+
+            if (value->kind == Ast_Kind_Argument) {
+                if (((AstArgument *) value)->is_baked) {
+                    i--;
+                    max--;
+                    bh_arr_deleten(args->values, i, 1);
+                }
+            }
+        }
+    }
+
+    if (args->named_values != NULL) {
+        bh_arr_each(AstNamedValue *, p_named_value, args->named_values) {
+            AstNamedValue* named_value = *p_named_value;
+
+            if (named_value->value->kind == Ast_Kind_Argument) {
+                if (((AstArgument *) named_value->value)->is_baked) {
+                    // :ArgumentResolvingIsComplicated
+                    bh_arr_set_length(args->values, bh_arr_length(args->values) - 1);
+                    continue;
+                }
+            }
+
+            token_toggle_end(named_value->token);
+            i32 idx = lookup_idx_by_name(provider, named_value->token->text);
+            if (idx == -1) {
+                if (err_msg) *err_msg = bh_aprintf(global_scratch_allocator, "'%s' is not a valid named parameter here.", named_value->token->text);
+                token_toggle_end(named_value->token);
+                return 0;
+            }
+
+            // assert(idx < bh_arr_length(args->values));
+            if (idx >= bh_arr_length(args->values)) {
+                if (err_msg) *err_msg = bh_aprintf(global_scratch_allocator, "Error placing value with name '%s' at index '%d'.", named_value->token->text, idx);
+                token_toggle_end(named_value->token);
+                return 0;
+            }
+
+            if (args->values[idx] != NULL && args->values[idx] != named_value->value) {
+                if (err_msg) *err_msg = bh_aprintf(global_scratch_allocator, "Multiple values given for parameter named '%s'.", named_value->token->text);
+                token_toggle_end(named_value->token);
+                return 0;
+            }
+
+            args->values[idx] = named_value->value;
+            token_toggle_end(named_value->token);
+        }
+    }
+
+    b32 success = 1;
+    fori (idx, 0, bh_arr_length(args->values)) {
+        if (args->values[idx] == NULL) args->values[idx] = (AstTyped *) lookup_default_value_by_idx(provider, idx);
+        if (args->values[idx] == NULL) {
+            if (insert_zero_values) {
+                assert(provider->token);
+                args->values[idx] = (AstTyped *) make_zero_value(context.ast_alloc, provider->token, NULL);
+            } else {
+                if (err_msg) *err_msg = bh_aprintf(global_scratch_allocator, "No value given for %d%s argument.", idx + 1, bh_num_suffix(idx + 1));
+                success = 0;
+            }
+        }
+    }
+
+    i32 maximum_arguments = maximum_argument_count(provider);
+    if (bh_arr_length(args->values) > maximum_arguments) {
+        if (err_msg) *err_msg = bh_aprintf(global_scratch_allocator, "Too many values provided. Expected at most %d.", maximum_arguments);
+        success = 0;
+    }
+
+    return success;
+}
+
+
+//
+// Argument checking
+//
+
+typedef enum ArgState {
+    AS_Expecting_Exact,
+    AS_Expecting_Typed_VA,
+    AS_Expecting_Untyped_VA,
+} ArgState;
+
+TypeMatch check_arguments_against_type(Arguments* args, TypeFunction* func_type, VarArgKind* va_kind,
+                                       OnyxToken* location, char* func_name, OnyxError* error) {
+    b32 permanent = location != NULL;
+    if (func_name == NULL) func_name = "UNKNOWN FUNCTION";
+
+    if (error) error->rank = Error_Critical;
+
+    bh_arr(AstArgument *) arg_arr = (bh_arr(AstArgument *)) args->values;
+    i32 arg_count = get_argument_buffer_size(func_type, args);
+
+    Type **formal_params = func_type->params;
+    Type* variadic_type = NULL;
+    i64 any_type_id = type_build_from_ast(context.ast_alloc, builtin_any_type)->id;
+
+    ArgState arg_state = AS_Expecting_Exact;
+    u32 arg_pos = 0;
+    while (1) {
+        switch (arg_state) {
+            case AS_Expecting_Exact: {
+                if (arg_pos >= func_type->param_count) goto type_checking_done;
+
+                if (formal_params[arg_pos]->kind == Type_Kind_VarArgs) {
+                    variadic_type = formal_params[arg_pos]->VarArgs.elem;
+                    arg_state = AS_Expecting_Typed_VA;
+                    continue;
+                }
+
+                if ((i16) arg_pos == func_type->vararg_arg_pos) {
+                    arg_state = AS_Expecting_Untyped_VA;
+                    continue;
+                }
+
+                if (arg_pos >= (u32) bh_arr_length(arg_arr)) goto type_checking_done;
+
+                assert(arg_arr[arg_pos]->kind == Ast_Kind_Argument);
+                TypeMatch tm = unify_node_and_type_(&arg_arr[arg_pos]->value, formal_params[arg_pos], permanent);
+                if (tm == TYPE_MATCH_YIELD) return tm;
+                if (tm == TYPE_MATCH_FAILED) {
+                    if (error != NULL) {
+                        error->pos = arg_arr[arg_pos]->token->pos;
+                        error->text = bh_aprintf(global_heap_allocator,
+                                "The procedure '%s' expects a value of type '%s' for %d%s parameter, got '%s'.",
+                                func_name,
+                                type_get_name(formal_params[arg_pos]),
+                                arg_pos + 1,
+                                bh_num_suffix(arg_pos + 1),
+                                node_get_type_name(arg_arr[arg_pos]->value));
+                    }
+                    return tm;
+                }
+
+                if (arg_arr[arg_pos]->value->type && arg_arr[arg_pos]->value->type->id != any_type_id && formal_params[arg_pos]->id == any_type_id) {
+                    resolve_expression_type(arg_arr[arg_pos]->value);
+                    if (error != NULL) {
+                        arg_arr[arg_pos]->pass_as_any = 1;
+                    }
+                }
+
+                arg_arr[arg_pos]->va_kind = VA_Kind_Not_VA;
+                break;
+            }
+
+            case AS_Expecting_Typed_VA: {
+                if (variadic_type->id == any_type_id) *va_kind = VA_Kind_Any;
+
+                if (arg_pos >= (u32) bh_arr_length(arg_arr)) goto type_checking_done;
+
+                if (variadic_type->id == any_type_id) {
+                    resolve_expression_type(arg_arr[arg_pos]->value);
+                    if (arg_arr[arg_pos]->value->type == NULL) {
+                        if (error != NULL) {
+                            error->pos = arg_arr[arg_pos]->token->pos;
+                            error->text = "Unable to resolve type of argument.";
+                        }
+                        return TYPE_MATCH_FAILED;
+                    }
+
+                    arg_arr[arg_pos]->va_kind = VA_Kind_Any; 
+                    break;
+                }
+
+                *va_kind = VA_Kind_Typed;
+
+                assert(arg_arr[arg_pos]->kind == Ast_Kind_Argument);
+                TypeMatch tm = unify_node_and_type_(&arg_arr[arg_pos]->value, variadic_type, permanent);
+                if (tm == TYPE_MATCH_YIELD) return tm;
+                if (tm == TYPE_MATCH_FAILED) {
+                    if (error != NULL) {
+                        error->pos = arg_arr[arg_pos]->token->pos,
+                        error->text = bh_aprintf(global_heap_allocator,
+                            "The procedure '%s' expects a value of type '%s' for the variadic parameter, got '%s'.",
+                            func_name,
+                            type_get_name(variadic_type),
+                            node_get_type_name(arg_arr[arg_pos]->value));
+                    }
+                    return tm;
+                }
+
+                arg_arr[arg_pos]->va_kind = VA_Kind_Typed;
+                break;
+            }
+
+            case AS_Expecting_Untyped_VA: {
+                *va_kind = VA_Kind_Untyped;
+
+                if (arg_pos >= (u32) bh_arr_length(arg_arr)) goto type_checking_done;
+
+                assert(arg_arr[arg_pos]->kind == Ast_Kind_Argument);
+                resolve_expression_type(arg_arr[arg_pos]->value);
+                if (arg_arr[arg_pos]->value->type == NULL) {
+                    if (error != NULL) {
+                        error->pos = arg_arr[arg_pos]->token->pos;
+                        error->text = "Unable to resolve type for argument.";
+                    }
+                    return TYPE_MATCH_FAILED;
+                }
+
+                arg_arr[arg_pos]->va_kind = VA_Kind_Untyped;
+                break;
+            }
+        }
+
+        arg_pos++;
+    }
+
+type_checking_done:
+    if (arg_pos < func_type->needed_param_count) {
+        if (error != NULL) {
+            error->pos = location->pos;
+            error->text = "Too few arguments to function call.";
+        }
+        return TYPE_MATCH_FAILED;
+    }
+
+    if (arg_pos < (u32) arg_count) {
+        if (error != NULL) {
+            error->pos = location->pos;
+            error->text = bh_aprintf(global_heap_allocator, "Too many arguments to function call. %d %d", arg_pos, arg_count);
+        }
+        return TYPE_MATCH_FAILED;
+    }
+
+    return TYPE_MATCH_SUCCESS;
+}
+        
+
+
+
+
+//
+// String parsing helpers
+//
+u32 char_to_base16_value(char x) {
+    if (x >= '0' && x <= '9') return (u32) (x - '0');
+    if (x >= 'A' && x <= 'F') return (u32) (x - 'A' + 10);
+    if (x >= 'a' && x <= 'f') return (u32) (x - 'a' + 10);
+    return 0xffffffff;
+}
+
+i32 string_process_escape_seqs(char* dest, char* src, i32 len) {
+    i32 total_len = 0;
+    for (i32 i = 0; i < len; i++) {
+        if (src[i] == '\\') {
+            i++;
+            switch (src[i]) {
+            case '0':  *dest++ = '\0'; total_len++; break;
+            case 'a':  *dest++ = '\a'; total_len++; break;
+            case 'b':  *dest++ = '\b'; total_len++; break;
+            case 'f':  *dest++ = '\f'; total_len++; break;
+            case 'n':  *dest++ = '\n'; total_len++; break;
+            case 't':  *dest++ = '\t'; total_len++; break;
+            case 'r':  *dest++ = '\r'; total_len++; break;
+            case 'v':  *dest++ = '\v'; total_len++; break;
+            case 'e':  *dest++ = '\e'; total_len++; break;
+            case '"':  *dest++ = '"';  total_len++; break;
+            case '\\': *dest++ = '\\'; total_len++; break;
+            case 'x': {
+                u8 ch1 = src[i + 1];
+                u8 ch2 = src[i + 2];
+                *dest++ = (i8) (char_to_base16_value(ch1) << 4 | char_to_base16_value(ch2));
+                total_len++;
+                i += 2;
+                break;
+            }
+            default:  *dest++ = '\\';
+                      *dest++ = src[i];
+                      total_len += 2;
+            }
+        } else {
+            *dest++ = src[i];
+            total_len += 1;
+        }
+    }
+
+    // NOTE: Gotta null terminate
+    *dest = 0;
+
+    return total_len;
+}
+
+static Scope **get_scope_from_node_helper(AstNode *node) {
+    b32 used_pointer = 0;
+
+    while (1) {
+        if (!node) return NULL;
+
+        switch (node->kind) {
+            case Ast_Kind_Type_Raw_Alias: node = (AstNode *) ((AstTypeRawAlias *) node)->to->ast_type; break;
+            case Ast_Kind_Type_Alias:     node = (AstNode *) ((AstTypeAlias *) node)->to; break;
+            case Ast_Kind_Alias:          node = (AstNode *) ((AstAlias *) node)->alias; break;
+            case Ast_Kind_Pointer_Type: {
+                if (used_pointer) goto all_types_peeled_off;
+                used_pointer = 1;
+
+                node = (AstNode *) ((AstPointerType *) node)->elem;
+                break;
+            }
+
+            default: goto all_types_peeled_off;
+        }
+    }
+
+all_types_peeled_off:
+    if (!node) return NULL;
+
+    switch (node->kind) {
+        case Ast_Kind_Package: {
+            AstPackage* package = (AstPackage *) node;
+            if (package->package == NULL) return NULL;
+
+            return &package->package->scope;
+        } 
+
+        case Ast_Kind_Enum_Type: {
+            AstEnumType* etype = (AstEnumType *) node;
+            return &etype->scope;
+        }
+
+        case Ast_Kind_Struct_Type: {
+            AstStructType* stype = (AstStructType *) node;
+            return &stype->scope;
+        }
+
+        case Ast_Kind_Poly_Struct_Type: {
+            AstPolyStructType* pstype = (AstPolyStructType *) node;
+            AstStructType* stype = pstype->base_struct;
+            return &stype->scope;
+        }
+    }
+
+    return NULL;
+}
+
+Scope *get_scope_from_node(AstNode *node) {
+    if (!node) return NULL;
+
+    Scope **pscope = get_scope_from_node_helper(node);
+    if (!pscope) return NULL;
+    return *pscope;
+}
+
+Scope *get_scope_from_node_or_create(AstNode *node) {
+    if (!node) return NULL;
+
+    Scope **pscope = get_scope_from_node_helper(node);
+    if (!pscope) return NULL;
+
+    // Create the scope if it does not exist.
+    // This uses a NULL parent, which I think is what 
+    // is used in other parts of the compiler for struct/enum
+    // scopes?
+    if (!*pscope) {
+        assert(node->token);
+        *pscope = scope_create(context.ast_alloc, NULL, node->token->pos);
+    }
+
+    return *pscope;
+}
+
+u32 levenshtein_distance(const char *str1, const char *str2) {
+    i32 m = strlen(str1) + 1;
+    i32 n = strlen(str2) + 1;
+
+    i32 *d = bh_alloc_array(global_scratch_allocator, i32, m * n);
+    fori (i, 0, m * n) d[i] = 0;
+
+    fori (i, 0, m) d[i * n + 0] = i;
+    fori (j, 0, n) d[0 * n + j] = j;
+
+    fori (j, 1, n) {
+        fori (i, 1, m) {
+            i32 subst_cost = str1[i - 1] == str2[j - 1] ? 0 : 1;
+
+            i32 a = d[(i - 1) * n + j] + 1;
+            i32 b = d[i * n + (j - 1)] + 1;
+            i32 c = d[(i - 1) * n + (j - 1)] + subst_cost;
+
+            d[i * n + j] = bh_min(bh_min(a, b), c);
+        }
+    }
+
+    return d[m * n - 1];
+}
+
+char *find_closest_symbol_in_scope(Scope *scope, char *sym, u32 *out_distance) {
+    *out_distance = 0x7fffffff;
+
+    if (scope == NULL) return NULL;
+
+    char* closest = NULL;
+    fori (i, 0, shlen(scope->symbols)) {
+        if (scope->symbols[i].value->flags & Ast_Flag_Symbol_Invisible) continue;
+
+        char *key = scope->symbols[i].key;
+        u32 d = levenshtein_distance(key, sym); 
+        if (d < *out_distance) {
+            *out_distance = d;
+            closest = (char *) key;
+        }
+    }
+
+    return closest;
+}
+
+char *find_closest_symbol_in_scope_and_parents(Scope *scope, char *sym) {
+    u32 min_dist = 0x7fffffff;
+    u32 tmp_dist; 
+
+    char *closest = NULL;
+    while (scope != NULL) {
+        char *tmp_closest = find_closest_symbol_in_scope(scope, sym, &tmp_dist);
+        if (tmp_dist < min_dist) {
+            min_dist = tmp_dist;
+            closest = tmp_closest;
+        }
+
+        scope = scope->parent;
+    }
+
+    return closest;
+}
+        
+char *find_closest_symbol_in_node(AstNode* node, char *sym) {
+    Scope *scope = get_scope_from_node(node);
+    if (!scope) {
+        if (node->kind == Ast_Kind_Poly_Call_Type) {
+            AstPolyCallType* pcall = (AstPolyCallType *) node;
+            return find_closest_symbol_in_node((AstNode *) pcall->callee, sym);
+        }
+
+        return NULL;
+    }
+
+    u32 dist;
+    return find_closest_symbol_in_scope(scope, sym, &dist);
+}
+
diff --git a/compiler/src/wasm_emit.c b/compiler/src/wasm_emit.c
new file mode 100644 (file)
index 0000000..0dc1c4a
--- /dev/null
@@ -0,0 +1,4588 @@
+//
+// There are several things I'm seeing in this file that I want to clean up.
+// They are:
+//  [x] remove the need to know if the stack is needed before generating the function.
+//      Just leave 5 nops at the beginning because they will be automatically removed
+//      by the WASM outputter.
+//  [x] remove the need to have "allocate_exprs" on blocks and in functions. This will
+//      be easy once the above is done.
+//  [x] there should be a better way to emit pending deferred statements because there
+//      is some code duplication between emit_return and emit_structured_jump.
+//  [ ] Change the calling convention so it is easier to use from both JS and in the compiler.
+
+
+
+
+#define BH_DEBUG
+#include "wasm_emit.h"
+#include "utils.h"
+
+#define WASM_TYPE_INT32   0x7F
+#define WASM_TYPE_INT64   0x7E
+#define WASM_TYPE_FLOAT32 0x7D
+#define WASM_TYPE_FLOAT64 0x7C
+#define WASM_TYPE_VAR128  0x7B
+#define WASM_TYPE_PTR     WASM_TYPE_INT32
+#define WASM_TYPE_FUNC    WASM_TYPE_INT32
+#define WASM_TYPE_VOID    0x00
+
+static WasmType onyx_type_to_wasm_type(Type* type) {
+    if (type->kind == Type_Kind_Struct) {
+        if (type_linear_member_count(type) == 1) {
+            return onyx_type_to_wasm_type(type->Struct.linear_members[0].type);
+        }
+        
+        return WASM_TYPE_VOID;
+    }
+
+    if (type->kind == Type_Kind_Slice) {
+        return WASM_TYPE_VOID;
+    }
+
+    if (type->kind == Type_Kind_Compound) {
+        return WASM_TYPE_VOID;
+    }
+
+    if (type->kind == Type_Kind_Enum) {
+        return onyx_type_to_wasm_type(type->Enum.backing);
+    }
+
+    if (type->kind == Type_Kind_Distinct) {
+        return onyx_type_to_wasm_type(type->Distinct.base_type);
+    }
+
+    if (type->kind == Type_Kind_Pointer) {
+        return WASM_TYPE_PTR;
+    }
+
+    if (type->kind == Type_Kind_Array) {
+        return WASM_TYPE_PTR;
+    }
+
+    if (type->kind == Type_Kind_Function) {
+        return WASM_TYPE_FUNC;
+    }
+
+    if (type->kind == Type_Kind_Basic) {
+        TypeBasic* basic = &type->Basic;
+        if (basic->flags & Basic_Flag_Boolean) return WASM_TYPE_INT32;
+        if (basic->flags & Basic_Flag_Integer) {
+            if (basic->size <= 4) return WASM_TYPE_INT32;
+            if (basic->size == 8) return WASM_TYPE_INT64;
+        }
+        if (basic->flags & Basic_Flag_Pointer) return WASM_TYPE_INT32;
+        if (basic->flags & Basic_Flag_Float) {
+            if (basic->size <= 4) return WASM_TYPE_FLOAT32;
+            if (basic->size == 8) return WASM_TYPE_FLOAT64;
+        }
+        if (basic->flags & Basic_Flag_SIMD) return WASM_TYPE_VAR128;
+        if (basic->flags & Basic_Flag_Type_Index) return WASM_TYPE_INT32;
+        if (basic->size == 0) return WASM_TYPE_VOID;
+    }
+
+    return WASM_TYPE_VOID;
+}
+
+static i32 generate_type_idx(OnyxWasmModule* mod, Type* ft);
+static i32 get_element_idx(OnyxWasmModule* mod, AstFunction* func);
+
+#define LOCAL_I32  0x000000000
+#define LOCAL_I64  0x100000000
+#define LOCAL_F32  0x300000000
+#define LOCAL_F64  0x700000000
+#define LOCAL_V128 0xf00000000
+
+static b32 local_is_wasm_local(AstTyped* local) {
+    if (local->kind == Ast_Kind_Local && local->flags & Ast_Flag_Address_Taken) return 0;
+    if (local->type->kind == Type_Kind_Basic) return 1;
+    if (local->type->kind == Type_Kind_Enum && local->type->Enum.backing->kind == Type_Kind_Basic) return 1;
+    if (local->type->kind == Type_Kind_Distinct && local->type->Distinct.base_type->kind == Type_Kind_Basic) return 1;
+    if (local->type->kind == Type_Kind_Pointer) return 1;
+    return 0;
+}
+
+static u64 local_raw_allocate(LocalAllocator* la, WasmType wt) {
+    i32 idx = 0;
+    if (wt == WASM_TYPE_INT32)   idx = 0;
+    if (wt == WASM_TYPE_INT64)   idx = 1;
+    if (wt == WASM_TYPE_FLOAT32) idx = 2;
+    if (wt == WASM_TYPE_FLOAT64) idx = 3;
+    if (wt == WASM_TYPE_VAR128)  idx = 4;
+
+    u64 flag_bits = LOCAL_IS_WASM;
+    if (wt == WASM_TYPE_INT32)   flag_bits |= LOCAL_I32;
+    if (wt == WASM_TYPE_INT64)   flag_bits |= LOCAL_I64;
+    if (wt == WASM_TYPE_FLOAT32) flag_bits |= LOCAL_F32;
+    if (wt == WASM_TYPE_FLOAT64) flag_bits |= LOCAL_F64;
+    if (wt == WASM_TYPE_VAR128)  flag_bits |= LOCAL_V128;
+
+    if (la->freed[idx] > 0) {
+        la->freed[idx]--;
+        return flag_bits | ((u64) (la->allocated[idx] - la->freed[idx] - 1 + la->param_count));
+
+    } else {
+        la->allocated[idx]++;
+        return flag_bits | ((u64) (la->allocated[idx] - 1 + la->param_count));
+    }
+}
+
+static void local_raw_free(LocalAllocator* la, WasmType wt) {
+    i32 idx = 0;
+
+    if (wt == WASM_TYPE_INT32)   idx = 0;
+    if (wt == WASM_TYPE_INT64)   idx = 1;
+    if (wt == WASM_TYPE_FLOAT32) idx = 2;
+    if (wt == WASM_TYPE_FLOAT64) idx = 3;
+    if (wt == WASM_TYPE_VAR128)  idx = 4;
+
+    assert(la->allocated[idx] > 0 && la->freed[idx] < la->allocated[idx]);
+
+    la->freed[idx]++;
+}
+
+static u64 local_allocate(LocalAllocator* la, AstTyped* local) {
+    if (local_is_wasm_local(local)) {
+        WasmType wt = onyx_type_to_wasm_type(local->type);
+        return local_raw_allocate(la, wt);
+
+    } else {
+        u32 size = type_size_of(local->type);
+        u32 alignment = type_alignment_of(local->type);
+
+        bh_align(la->curr_stack, alignment);
+
+        if (la->max_stack < la->curr_stack)
+            la->max_stack = la->curr_stack;
+
+        bh_align(size, alignment);
+
+        if (la->max_stack - la->curr_stack >= (i32) size) {
+            la->curr_stack += size;
+
+        } else {
+            la->max_stack += size - (la->max_stack - la->curr_stack);
+            la->curr_stack = la->max_stack;
+        }
+
+        return la->curr_stack - size;
+    }
+}
+
+static void local_free(LocalAllocator* la, AstTyped* local) {
+    if (local_is_wasm_local(local)) {
+        WasmType wt = onyx_type_to_wasm_type(local->type);
+        local_raw_free(la, wt);
+
+    } else {
+        u32 size = type_size_of(local->type);
+        u32 alignment = type_alignment_of(local->type);
+        bh_align(size, alignment);
+
+        la->curr_stack -= size;
+    }
+}
+
+static u64 local_lookup_idx(LocalAllocator* la, u64 value) {
+    assert(value & LOCAL_IS_WASM);
+
+    u32 idx = value & 0xFFFFFFFF;
+    if (value & 0x100000000) idx += la->allocated[0];
+    if (value & 0x200000000) idx += la->allocated[1];
+    if (value & 0x400000000) idx += la->allocated[2];
+    if (value & 0x800000000) idx += la->allocated[3];
+
+    return (u64) idx;
+}
+
+
+static inline b32 should_emit_function(AstFunction* fd) {
+    // NOTE: Don't output intrinsic functions
+    if (fd->is_intrinsic) return 0;
+
+    // NOTE: Don't output functions that are not used, only if
+    // they are also not exported.
+    if ((fd->flags & Ast_Flag_Function_Used) == 0) {
+        if (fd->is_exported || bh_arr_length(fd->tags) > 0) {
+            return 1;
+        } else {
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+
+//
+// Debug Info Generation
+//
+
+#ifdef ENABLE_DEBUG_INFO
+
+static u32 debug_introduce_symbol(OnyxWasmModule *mod, OnyxToken *token, DebugSymLoc loc, u64 num, Type* type) {
+
+    u32 id = mod->debug_context->next_sym_id++;
+
+    DebugSymInfo sym_info;
+    sym_info.sym_id = id;
+    sym_info.location_type = loc;
+    sym_info.location_num  = num;
+    sym_info.type          = type->id;
+
+    if (token) {
+        token_toggle_end(token);
+        sym_info.name = bh_strdup(context.ast_alloc, token->text);
+        token_toggle_end(token);
+    } else {
+        sym_info.name = NULL;
+    }
+
+    bh_arr_push(mod->debug_context->sym_info, sym_info);
+
+    if (loc == DSL_REGISTER) {
+        assert(mod->local_alloc);
+        DebugSymPatch patch;
+        patch.func_idx = mod->current_func_idx;
+        patch.sym_id = id;
+        patch.local_idx = num;
+        bh_arr_push(mod->debug_context->sym_patches, patch);
+    }
+
+    bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_SYM);
+    u32 leb_len=0;
+    u8 *bytes = uint_to_uleb128(id, &leb_len);
+    bh_buffer_append(&mod->debug_context->op_buffer, bytes, leb_len);
+
+    mod->debug_context->last_op_was_rep = 0;
+    return id;
+}
+
+static u32 debug_get_file_id(OnyxWasmModule *mod, const char *name) {
+    assert(mod && mod->debug_context);
+
+    i32 index = shgeti(mod->debug_context->file_info, name);
+    if (index == -1) {
+        u32 id = mod->debug_context->next_file_id++;
+        DebugFileInfo file_info;
+        file_info.file_id = id;
+
+        bh_arr_each(bh_file_contents, fc, context.loaded_files) {
+            if (!strcmp(fc->filename, name)) {
+                file_info.line_count = fc->line_count;
+            }
+        }
+        shput(mod->debug_context->file_info, name, file_info);
+
+        return id;
+    }
+
+    return mod->debug_context->file_info[index].value.file_id;
+}
+
+static void debug_set_position(OnyxWasmModule *mod, OnyxToken *token) {
+    i32 file_id = debug_get_file_id(mod, token->pos.filename);
+
+    bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_SET);
+    mod->debug_context->last_op_was_rep = 0;
+
+    u32 leb_len=0;
+    u8 *bytes = uint_to_uleb128(file_id, &leb_len);
+    bh_buffer_append(&mod->debug_context->op_buffer, bytes, leb_len);
+
+    bytes = uint_to_uleb128(token->pos.line, &leb_len);
+    bh_buffer_append(&mod->debug_context->op_buffer, bytes, leb_len);
+
+    mod->debug_context->last_token = token;
+}
+
+// Called for every instruction being emitted
+// This has to emit either:
+//    - INC
+//    - DEC
+//    - REP
+//    - SET, REP 0
+static void debug_emit_instruction(OnyxWasmModule *mod, OnyxToken *token) {
+    DebugContext *ctx = mod->debug_context; 
+    assert(ctx && ctx->last_token);
+
+    // Sanity check
+    if (ctx->last_op_was_rep) {
+        assert((ctx->op_buffer.data[ctx->op_buffer.length - 1] & DOT_REP) == DOT_REP);
+    }
+    i32 file_id, old_file_id;
+
+    b32 repeat_previous = 0;
+    if (!token || !token->pos.filename) {
+        repeat_previous = 1;
+
+    } else {
+        file_id = debug_get_file_id(mod, token->pos.filename);
+        old_file_id = debug_get_file_id(mod, ctx->last_token->pos.filename);
+        if (old_file_id == file_id && token->pos.line == ctx->last_token->pos.line) {
+            repeat_previous = 1;
+        }
+    }
+    
+    if (repeat_previous) {
+        // Output / increment REP instruction
+        if (ctx->last_op_was_rep) {
+            u8 rep_count = ctx->op_buffer.data[ctx->op_buffer.length - 1] & 0b00111111;
+            if (rep_count != 63) {
+                rep_count += 1;
+                ctx->op_buffer.data[ctx->op_buffer.length - 1] = DOT_REP | rep_count;
+                return;
+            }
+        }
+
+        bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_REP);
+        ctx->last_op_was_rep = 1;
+        return;
+    }
+
+    // At this point, token is definitely non-null.
+    ctx->last_op_was_rep = 0;
+
+    if (old_file_id == file_id) {
+        // We see if we can INC/DEC to get to the line number
+        if (ctx->last_token->pos.line < token->pos.line) {
+            u32 diff = token->pos.line - ctx->last_token->pos.line;
+            if (diff <= 64) {
+                bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_INC | (diff - 1));
+                goto done;
+            }
+        }
+
+        if (ctx->last_token->pos.line > token->pos.line) {
+            u32 diff = ctx->last_token->pos.line - token->pos.line;
+            if (diff <= 64) {
+                bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_DEC | (diff - 1));
+                goto done;
+            }
+        }
+    }
+
+    // Otherwise, we need to output a SET, followed by a REP 0,
+    // which is what set_position does.
+    debug_set_position(mod, token);
+    bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_REP);
+    ctx->last_op_was_rep = 1;
+
+  done:
+    ctx->last_token = token;
+}
+
+static void debug_begin_function(OnyxWasmModule *mod, u32 func_idx, OnyxToken *token, char *name) {
+    u32 file_id = debug_get_file_id(mod, token->pos.filename);
+    u32 line    = token->pos.line;
+
+    assert(mod->debug_context);
+
+    DebugFuncContext func;
+    func.func_index = func_idx;
+    func.file_id = file_id;
+    func.line    = line;
+    func.op_offset = mod->debug_context->op_buffer.length;
+    func.stack_ptr_idx = 0;
+    func.name_length = strlen(name);
+    func.name = name;
+    bh_arr_push(mod->debug_context->funcs, func);
+
+    bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_PUSHF);
+    debug_set_position(mod, token);
+}
+
+static void debug_end_function(OnyxWasmModule *mod) {
+    bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_POPF);
+    bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_END);
+    mod->debug_context->last_op_was_rep = 0;
+    mod->debug_context->last_token = NULL;
+}
+
+static void debug_enter_symbol_frame(OnyxWasmModule *mod) {
+    bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_PUSHF);
+    mod->debug_context->last_op_was_rep = 0;
+}
+
+static void debug_leave_symbol_frame(OnyxWasmModule *mod) {
+    bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_POPF);
+    mod->debug_context->last_op_was_rep = 0;
+}
+
+#else
+
+#define debug_introduce_symbol(mod, name, loc, num, type) (void)0
+#define debug_get_file_id(mod, name) (void)0
+#define debug_set_position(mod, token) (void)0
+#define debug_emit_instruction(mod, token) (void)0
+#define debug_begin_function(mod, idx, token, name) (void)0
+#define debug_end_function(mod) (void)0
+#define debug_enter_symbol_frame(mod) (void)0
+#define debug_leave_symbol_frame(mod) (void)0
+
+#endif
+
+
+typedef enum StructuredBlockType StructuredBlockType;
+enum StructuredBlockType {
+    SBT_Basic_Block,       // Cannot be targeted using jump
+    SBT_Breakable_Block,   // Targeted using break
+    SBT_Continue_Block,    // Targeted using continue
+    SBT_Fallthrough_Block, // Targeted using fallthrough
+    SBT_Return_Block,      // Targeted using return, (used for expression macros)
+
+    SBT_Basic_If,          // Cannot be targeted using jump
+    SBT_Breakable_If,      // Targeted using break
+
+    SBT_Basic_Loop,        // Cannot be targeted using jump
+    SBT_Continue_Loop,     // Targeted using continue
+
+    SBT_Count,
+};
+
+#ifdef ENABLE_DEBUG_INFO
+    #define WI(token, instr) (debug_emit_instruction(mod, token),        bh_arr_push(code, ((WasmInstruction){ instr, 0x00 })))
+    #define WID(token, instr, data) (debug_emit_instruction(mod, token), bh_arr_push(code, ((WasmInstruction){ instr, data })))
+    #define WIL(token, instr, data) (debug_emit_instruction(mod, token), bh_arr_push(code, ((WasmInstruction){ instr, { .l = data } })))
+    #define WIP(token, instr, data) (debug_emit_instruction(mod, token), bh_arr_push(code, ((WasmInstruction){ instr, { .p = data } })))
+    #define WIR(token, full_instr)  (debug_emit_instruction(mod, token), bh_arr_push(code, full_instr))
+#else
+    #define WI(token, instr) (bh_arr_push(code, ((WasmInstruction){ instr, 0x00 })))
+    #define WID(token, instr, data) (bh_arr_push(code, ((WasmInstruction){ instr, data })))
+    #define WIL(token, instr, data) (bh_arr_push(code, ((WasmInstruction){ instr, { .l = data } })))
+    #define WIP(token, instr, data) (bh_arr_push(code, ((WasmInstruction){ instr, { .p = data } })))
+    #define WIR(token, full_instr)  (bh_arr_push(code, full_instr))
+#endif
+
+#define EMIT_FUNC(kind, ...) static void emit_ ## kind (OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, __VA_ARGS__)
+#define EMIT_FUNC_NO_ARGS(kind) static void emit_ ## kind (OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode)
+#define STACK_SWAP(token, type1, type2) { \
+    u64 t0 = local_raw_allocate(mod->local_alloc, type1); \
+    u64 t1 = local_raw_allocate(mod->local_alloc, type2); \
+                                                          \
+    WIL(token, WI_LOCAL_SET, t0);                         \
+    WIL(token, WI_LOCAL_SET, t1);                         \
+    WIL(token, WI_LOCAL_GET, t0);                         \
+    WIL(token, WI_LOCAL_GET, t1);                         \
+                                                          \
+    local_raw_free(mod->local_alloc, type1);              \
+    local_raw_free(mod->local_alloc, type2);              \
+    }
+#define SUBMIT_PATCH(patch_arr, offset) bh_arr_push((patch_arr), ((PatchInfo) { bh_arr_length(code) - offset }))
+#define NEXT_DATA_ID(mod)               ((u32) bh_arr_length((mod)->data) + 1)
+
+EMIT_FUNC(function_body,                 AstFunction* fd);
+EMIT_FUNC(block,                         AstBlock* block, b32 generate_block_headers);
+EMIT_FUNC(statement,                     AstNode* stmt);
+EMIT_FUNC(local_allocation,              AstTyped* stmt);
+EMIT_FUNC_NO_ARGS(free_local_allocations);
+EMIT_FUNC(data_relocation,               u32 data_id);
+EMIT_FUNC(assignment,                    AstBinaryOp* assign);
+EMIT_FUNC(assignment_of_array,           AstTyped* left, AstTyped* right);
+EMIT_FUNC(compound_assignment,           AstBinaryOp* assign);
+EMIT_FUNC(store_instruction,             Type* type, u32 offset);
+EMIT_FUNC(load_instruction,              Type* type, u32 offset);
+EMIT_FUNC(if,                            AstIfWhile* if_node);
+EMIT_FUNC(while,                         AstIfWhile* while_node);
+EMIT_FUNC(for,                           AstFor* for_node);
+EMIT_FUNC(switch,                        AstSwitch* switch_node);
+EMIT_FUNC(defer,                         AstDefer* defer);
+EMIT_FUNC(defer_code,                    WasmInstruction* deferred_code, u32 code_count);
+EMIT_FUNC(deferred_stmt,                 DeferredStmt deferred_stmt);
+EMIT_FUNC_NO_ARGS(deferred_stmts);
+EMIT_FUNC(remove_directive,              AstDirectiveRemove* remove);
+EMIT_FUNC(binop,                         AstBinaryOp* binop);
+EMIT_FUNC(unaryop,                       AstUnaryOp* unop);
+EMIT_FUNC(call,                          AstCall* call);
+EMIT_FUNC(intrinsic_call,                AstCall* call);
+EMIT_FUNC(subscript_location,            AstSubscript* sub, u64* offset_return);
+EMIT_FUNC(field_access_location,         AstFieldAccess* field, u64* offset_return);
+EMIT_FUNC(local_location,                AstLocal* local, u64* offset_return);
+EMIT_FUNC(memory_reservation_location,   AstMemRes* memres);
+EMIT_FUNC(location_return_offset,        AstTyped* expr, u64* offset_return);
+EMIT_FUNC(location,                      AstTyped* expr);
+EMIT_FUNC(compound_load,                 Type* type, u64 offset);
+EMIT_FUNC(struct_lval,                   AstTyped* lval);
+EMIT_FUNC(struct_literal,                AstStructLiteral* sl);
+EMIT_FUNC(compound_store,                Type* type, u64 offset, b32 location_first);
+EMIT_FUNC(array_store,                   Type* type, u32 offset);
+EMIT_FUNC(array_literal,                 AstArrayLiteral* al);
+EMIT_FUNC(range_literal,                 AstRangeLiteral* range);
+EMIT_FUNC(if_expression,                 AstIfExpression* if_expr);
+EMIT_FUNC(do_block,                      AstDoBlock* doblock);
+EMIT_FUNC(expression,                    AstTyped* expr);
+EMIT_FUNC(cast,                          AstUnaryOp* cast);
+EMIT_FUNC(return,                        AstReturn* ret);
+EMIT_FUNC(stack_enter,                   u64 stacksize);
+EMIT_FUNC(zero_value,                    WasmType wt);
+EMIT_FUNC(zero_value_for_type,           Type* type, OnyxToken* where);
+
+EMIT_FUNC(enter_structured_block,        StructuredBlockType sbt, OnyxToken* block_token);
+EMIT_FUNC_NO_ARGS(leave_structured_block);
+
+static u32 emit_data_entry(OnyxWasmModule *mod, WasmDatum *datum);
+
+static void emit_constexpr(ConstExprContext *ctx, AstTyped *node, u32 offset);
+static b32 emit_constexpr_(ConstExprContext *ctx, AstTyped *node, u32 offset);
+
+#include "wasm_intrinsics.h"
+#include "wasm_type_table.h"
+
+EMIT_FUNC(function_body, AstFunction* fd) {
+    if (fd->body == NULL) return;
+
+    emit_block(mod, pcode, fd->body, 0);
+}
+
+EMIT_FUNC(block, AstBlock* block, b32 generate_block_headers) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    generate_block_headers = generate_block_headers && (block->rules & Block_Rule_Emit_Instructions);
+
+    if (generate_block_headers) {
+        emit_enter_structured_block(mod, &code, (block->rules & Block_Rule_Override_Return)
+                                                ? SBT_Return_Block
+                                                : SBT_Breakable_Block,
+                                                block->token);
+        debug_enter_symbol_frame(mod);
+    }
+
+    forll (AstNode, stmt, block->body, next) {
+        emit_statement(mod, &code, stmt);
+    }
+
+    // HACK: I'm not convinced this is the best way to handle this case. Essentially, if
+    // a deferred statement uses a local that is freed from the a macro block, then the
+    // local won't be valid anymore and could have garbage data. This ensures that the
+    // freeing of the local variables and flushing of the deferred statements happen together,
+    // so a deferred statement can never try to use a local that has been freed.
+    //                                                                   - brendanfh 2021/08/29
+    if (block->rules & Block_Rule_Clear_Defer) {
+        emit_deferred_stmts(mod, &code);
+
+        // if (block->rules & Block_Rule_New_Scope)
+        emit_free_local_allocations(mod, &code);
+    }
+
+    if (generate_block_headers) {
+        emit_leave_structured_block(mod, &code);
+        debug_leave_symbol_frame(mod);
+    }
+
+    *pcode = code;
+}
+
+EMIT_FUNC(enter_structured_block, StructuredBlockType sbt, OnyxToken* token) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    static const StructuredBlockType jump_numbers[SBT_Count] = {
+        /* SBT_Basic_Block */       0,
+        /* SBT_Breakable_Block */   1,
+        /* SBT_Continue_Block */    2,
+        /* SBT_Fallthrough_Block */ 3,
+        /* SBT_Return_Block */      4,
+
+        /* SBT_Basic_If */          0,
+        /* SBT_Breakable_If */      1,
+
+        /* SBT_Basic_Loop */        0,
+        /* SBT_Continue_Loop */     2,
+    };
+
+    static const WasmInstructionType block_instrs[SBT_Count] = {
+        /* SBT_Basic_Block */       WI_BLOCK_START,
+        /* SBT_Breakable_Block */   WI_BLOCK_START,
+        /* SBT_Continue_Block */    WI_BLOCK_START,
+        /* SBT_Fallthrough_Block */ WI_BLOCK_START,
+        /* SBT_Return_Block */      WI_BLOCK_START,
+
+        /* SBT_Basic_If */          WI_IF_START,
+        /* SBT_Breakable_If */      WI_IF_START,
+
+        /* SBT_Basic_Loop */        WI_LOOP_START,
+        /* SBT_Continue_Loop */     WI_LOOP_START,
+    };
+
+
+    WID(token, block_instrs[sbt], 0x40);
+    bh_arr_push(mod->structured_jump_target, jump_numbers[sbt]);
+
+    *pcode = code;
+}
+
+EMIT_FUNC_NO_ARGS(leave_structured_block) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    WI(NULL, WI_BLOCK_END);
+    bh_arr_pop(mod->structured_jump_target);
+
+    *pcode = code;
+}
+
+i64 get_structured_jump_label(OnyxWasmModule* mod, JumpType jump_type, u32 jump_count) {
+    // :CLEANUP These numbers should become constants because they are shared with
+    // enter_structured_block's definitions.
+    static const u8 wants[Jump_Type_Count] = { 1, 2, 3, 4 };
+
+    i64 labelidx = 0;
+    u8 wanted = wants[jump_type];
+    b32 success = 0;
+
+    i32 len = bh_arr_length(mod->structured_jump_target) - 1;
+    for (u8* t = &bh_arr_last(mod->structured_jump_target); len >= 0; len--, t--) {
+        if (*t == wanted) jump_count--;
+        if (jump_count == 0) {
+            success = 1;
+            break;
+        }
+
+        labelidx++;
+    }
+
+    return (success == 0) ? -1 : labelidx;
+}
+
+EMIT_FUNC(structured_jump, AstJump* jump) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    i64 labelidx = get_structured_jump_label(mod, jump->jump, jump->count);
+
+    if (bh_arr_length(mod->deferred_stmts) > 0) {
+        i32 i = bh_arr_length(mod->deferred_stmts) - 1;
+        i32 d = bh_arr_length(mod->structured_jump_target) - (labelidx + 1);
+
+        while (i >= 0 && (i32) mod->deferred_stmts[i].depth > d) {
+            emit_deferred_stmt(mod, &code, mod->deferred_stmts[i]);
+            i--;
+        }
+    }
+
+    if (labelidx >= 0) {
+        // NOTE: If the previous instruction was a non conditional jump,
+        // don't emit another jump since it will never be reached.
+        if (bh_arr_last(code).type != WI_JUMP)
+            WID(jump->token, WI_JUMP, labelidx);
+    } else {
+        onyx_report_error(jump->token->pos, Error_Critical, "Invalid structured jump.");
+    }
+
+    *pcode = code;
+}
+
+EMIT_FUNC(statement, AstNode* stmt) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+#ifdef ENABLE_DEBUG_INFO
+    debug_set_position(mod, stmt->token);
+#endif
+
+    switch (stmt->kind) {
+        case Ast_Kind_Return:     emit_return(mod, &code, (AstReturn *) stmt); break;
+        case Ast_Kind_If:         emit_if(mod, &code, (AstIfWhile *) stmt); break;
+        case Ast_Kind_Static_If:  emit_if(mod, &code, (AstIfWhile *) stmt); break;
+        case Ast_Kind_While:      emit_while(mod, &code, (AstIfWhile *) stmt); break;
+        case Ast_Kind_For:        emit_for(mod, &code, (AstFor *) stmt); break;
+        case Ast_Kind_Switch:     emit_switch(mod, &code, (AstSwitch *) stmt); break;
+        case Ast_Kind_Jump:       emit_structured_jump(mod, &code, (AstJump *) stmt); break;
+        case Ast_Kind_Block:      emit_block(mod, &code, (AstBlock *) stmt, 1); break;
+        case Ast_Kind_Defer:      emit_defer(mod, &code, (AstDefer *) stmt); break;
+        case Ast_Kind_Local:      emit_local_allocation(mod, &code, (AstTyped *) stmt); break;
+
+        case Ast_Kind_Directive_Remove: emit_remove_directive(mod, &code, (AstDirectiveRemove *) stmt); break;
+        case Ast_Kind_Directive_Insert: break;
+
+        default:                  emit_expression(mod, &code, (AstTyped *) stmt); break;
+    }
+
+    *pcode = code;
+}
+
+EMIT_FUNC(local_allocation, AstTyped* stmt) {
+    u64 local_idx = local_allocate(mod->local_alloc, stmt);
+    bh_imap_put(&mod->local_map, (u64) stmt, local_idx);
+
+    if (local_is_wasm_local(stmt)) {
+        debug_introduce_symbol(mod, stmt->token, DSL_REGISTER, local_idx, stmt->type);
+    } else {
+        debug_introduce_symbol(mod, stmt->token, DSL_STACK, local_idx, stmt->type);
+    }
+
+    if (stmt->kind == Ast_Kind_Local && !(stmt->flags & Ast_Flag_Decl_Followed_By_Init)) {
+        bh_arr(WasmInstruction) code = *pcode;
+        if (local_is_wasm_local(stmt)) {
+            emit_zero_value(mod, &code, onyx_type_to_wasm_type(stmt->type));
+            WIL(stmt->token, WI_LOCAL_SET, local_idx);
+
+        } else {
+            emit_location(mod, &code, stmt);
+            WID(stmt->token, WI_I32_CONST, 0);
+            WID(stmt->token, WI_I32_CONST, type_size_of(stmt->type));
+            if (context.options->use_post_mvp_features) {
+                WID(stmt->token, WI_MEMORY_FILL, 0x00);
+            } else {
+                emit_intrinsic_memory_fill(mod, &code);
+            }
+        }
+
+        *pcode = code;
+    }
+
+    bh_arr_push(mod->local_allocations, ((AllocatedSpace) {
+        .depth = bh_arr_length(mod->structured_jump_target),
+        .expr  = stmt,
+    }));
+}
+
+EMIT_FUNC_NO_ARGS(free_local_allocations) {
+    if (bh_arr_length(mod->local_allocations) == 0) return;
+
+    u64 depth = bh_arr_length(mod->structured_jump_target);
+    while (bh_arr_length(mod->local_allocations) > 0 && bh_arr_last(mod->local_allocations).depth >= depth) {
+        // CHECK: Not sure this next line is okay to be here...
+        bh_imap_delete(&mod->local_map, (u64) bh_arr_last(mod->local_allocations).expr);
+
+        local_free(mod->local_alloc, bh_arr_last(mod->local_allocations).expr);
+        bh_arr_pop(mod->local_allocations);
+    }
+}
+
+EMIT_FUNC(data_relocation, u32 data_id) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    u32 instr_idx = bh_arr_length(code);
+    WID(NULL, WI_PTR_CONST, 0);
+    assert(mod->current_func_idx >= 0);
+
+    DatumPatchInfo patch;
+    patch.kind = Datum_Patch_Instruction;
+    patch.index = mod->current_func_idx;
+    patch.location = instr_idx;
+    patch.data_id = data_id;
+    patch.offset = 0;
+    bh_arr_push(mod->data_patches, patch);
+
+    *pcode = code;
+}
+
+EMIT_FUNC(assignment, AstBinaryOp* assign) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    if (assign->right->type->kind == Type_Kind_Array) {
+        emit_assignment_of_array(mod, &code, assign->left, assign->right);
+        *pcode = code;
+        return;
+    }
+
+    if (assign->right->type->kind == Type_Kind_Compound) {
+        emit_compound_assignment(mod, &code, assign);
+        *pcode = code;
+        return;
+    }
+
+    AstTyped* lval = assign->left;
+
+    if (lval->kind == Ast_Kind_Local || lval->kind == Ast_Kind_Param) {
+        if (bh_imap_get(&mod->local_map, (u64) lval) & LOCAL_IS_WASM) {
+            emit_expression(mod, &code, assign->right);
+
+            u64 localidx = bh_imap_get(&mod->local_map, (u64) lval);
+
+            if (lval->kind == Ast_Kind_Param && type_is_structlike_strict(lval->type)) {
+                u32 mem_count = type_structlike_mem_count(lval->type);
+                fori (i, 0, mem_count) WIL(assign->token, WI_LOCAL_SET, localidx + i);
+
+            } else {
+                WIL(assign->token, WI_LOCAL_SET, localidx);
+            }
+
+            *pcode = code;
+            return;
+        }
+    }
+
+    if (lval->kind == Ast_Kind_Field_Access) {
+        AstFieldAccess* fa = (AstFieldAccess *) lval;
+        if (fa->expr->kind == Ast_Kind_Param && type_is_structlike_strict(fa->expr->type)) {
+            emit_expression(mod, &code, assign->right);
+
+            u64 localidx = bh_imap_get(&mod->local_map, (u64) fa->expr);
+            WIL(assign->token, WI_LOCAL_SET, localidx + fa->idx);
+
+            *pcode = code;
+            return;
+        }
+    }
+
+    if (lval->kind == Ast_Kind_Global) {
+        emit_expression(mod, &code, assign->right);
+
+        i32 globalidx = (i32) bh_imap_get(&mod->index_map, (u64) lval);
+        WID(assign->token, WI_GLOBAL_SET, globalidx);
+
+        *pcode = code;
+        return;
+    }
+
+    u64 offset = 0;
+    emit_location_return_offset(mod, &code, lval, &offset);
+    emit_expression(mod, &code, assign->right);
+    emit_store_instruction(mod, &code, lval->type, offset);
+
+    *pcode = code;
+}
+
+EMIT_FUNC(assignment_of_array, AstTyped* left, AstTyped* right) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    Type* rtype = right->type;
+    assert(rtype->kind == Type_Kind_Array);
+
+    if (right->kind == Ast_Kind_Array_Literal) {
+        Type* elem_type = rtype;
+        u32 elem_count = 1;
+        while (elem_type->kind == Type_Kind_Array) {
+            elem_count *= elem_type->Array.count;
+            elem_type = elem_type->Array.elem;
+        }
+        u32 elem_size = type_size_of(elem_type);
+
+        u64 lptr_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+
+        emit_location(mod, &code, left);
+        WIL(left->token, WI_LOCAL_SET, lptr_local);
+
+        AstArrayLiteral* al = (AstArrayLiteral *) right;
+        fori (i, 0, elem_count) {
+            WIL(left->token, WI_LOCAL_GET, lptr_local);
+            emit_expression(mod, &code, al->values[i]);
+            emit_store_instruction(mod, &code, elem_type, i * elem_size);
+        }
+
+        local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+
+    } else {
+        u64 offset = 0;
+        emit_location_return_offset(mod, &code, left, &offset);
+        emit_expression(mod, &code, right);
+        emit_array_store(mod, &code, rtype, offset);
+    }
+
+    *pcode = code;
+    return;
+}
+
+EMIT_FUNC(compound_assignment, AstBinaryOp* assign) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    emit_expression(mod, &code, assign->right);
+
+    AstCompound* compound_lval = (AstCompound *) assign->left;
+    bh_arr_rev_each(AstTyped *, plval, compound_lval->exprs) {
+        AstTyped *lval = *plval;
+
+        if (type_is_structlike_strict(lval->type)) {
+            emit_struct_lval(mod, &code, lval);
+            continue;
+        }
+
+        if (lval->kind == Ast_Kind_Local || lval->kind == Ast_Kind_Param) {
+            if (bh_imap_get(&mod->local_map, (u64) lval) & LOCAL_IS_WASM) {
+                u64 localidx = bh_imap_get(&mod->local_map, (u64) lval);
+                WIL(assign->token, WI_LOCAL_SET, localidx);
+                continue;
+            }
+        }
+
+        WasmType wt = onyx_type_to_wasm_type(lval->type);
+        u64 expr_tmp = local_raw_allocate(mod->local_alloc, wt);
+        WIL(assign->token, WI_LOCAL_SET, expr_tmp);
+        u64 offset = 0;
+        emit_location_return_offset(mod, &code, lval, &offset);
+        WIL(assign->token, WI_LOCAL_GET, expr_tmp);
+
+        local_raw_free(mod->local_alloc, wt);
+        emit_store_instruction(mod, &code, lval->type, offset);
+    }
+
+    *pcode = code;
+    return;
+}
+
+EMIT_FUNC(store_instruction, Type* type, u32 offset) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    if (type_is_compound(type)) {
+        emit_compound_store(mod, pcode, type, offset, 0);
+        return;
+    }
+
+    if (type->kind == Type_Kind_Struct) {
+        assert(bh_arr_length(type->Struct.linear_members) == 1);
+        type = type->Struct.linear_members[0].type;
+    }
+
+    if (type->kind == Type_Kind_Array) {
+        emit_array_store(mod, pcode, type, offset);
+        return;
+    }
+
+    if (type->kind == Type_Kind_Enum)     type = type->Enum.backing;
+    if (type->kind == Type_Kind_Distinct) type = type->Distinct.base_type;
+    if (type->kind == Type_Kind_Function) type = &basic_types[Basic_Kind_U32];
+
+    u32 alignment = type_get_alignment_log2(type);
+
+    i32 store_size  = type_size_of(type);
+    i32 is_basic    = type->kind == Type_Kind_Basic || type->kind == Type_Kind_Pointer;
+
+    if (is_basic && (type->Basic.flags & Basic_Flag_Pointer)) {
+        WID(NULL, WI_I32_STORE, ((WasmInstructionData) { 2, offset }));
+    } else if (is_basic && ((type->Basic.flags & Basic_Flag_Integer)
+                         || (type->Basic.flags & Basic_Flag_Boolean)
+                         || (type->Basic.flags & Basic_Flag_Type_Index))) {
+        if      (store_size == 1)   WID(NULL, WI_I32_STORE_8,  ((WasmInstructionData) { alignment, offset }));
+        else if (store_size == 2)   WID(NULL, WI_I32_STORE_16, ((WasmInstructionData) { alignment, offset }));
+        else if (store_size == 4)   WID(NULL, WI_I32_STORE,    ((WasmInstructionData) { alignment, offset }));
+        else if (store_size == 8)   WID(NULL, WI_I64_STORE,    ((WasmInstructionData) { alignment, offset }));
+    } else if (is_basic && (type->Basic.flags & Basic_Flag_Float)) {
+        if      (store_size == 4)   WID(NULL, WI_F32_STORE, ((WasmInstructionData) { alignment, offset }));
+        else if (store_size == 8)   WID(NULL, WI_F64_STORE, ((WasmInstructionData) { alignment, offset }));
+    } else if (is_basic && (type->Basic.flags & Basic_Flag_SIMD)) {
+        WID(NULL, WI_V128_STORE, ((WasmInstructionData) { alignment, offset }));
+    } else {
+        onyx_report_error((OnyxFilePos) { 0 }, Error_Critical,
+            "Failed to generate store instruction for type '%s'.",
+            type_get_name(type));
+    }
+
+    *pcode = code;
+}
+
+EMIT_FUNC(load_instruction, Type* type, u32 offset) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    if (type_is_compound(type)) {
+        emit_compound_load(mod, pcode, type, offset);
+        return;
+    }
+
+    if (type->kind == Type_Kind_Struct) {
+        assert(bh_arr_length(type->Struct.linear_members) == 1);
+        type = type->Struct.linear_members[0].type;
+    }
+
+    if (type->kind == Type_Kind_Array) {
+        if (offset != 0) {
+            WID(NULL, WI_PTR_CONST, offset);
+            WI(NULL, WI_PTR_ADD);
+        }
+
+        *pcode = code;
+        return;
+    }
+
+    if (type->kind == Type_Kind_Enum)     type = type->Enum.backing;
+    if (type->kind == Type_Kind_Distinct) type = type->Distinct.base_type;
+    if (type->kind == Type_Kind_Function) type = &basic_types[Basic_Kind_U32];
+
+    i32 load_size   = type_size_of(type);
+    i32 is_basic    = type->kind == Type_Kind_Basic || type->kind == Type_Kind_Pointer;
+
+    WasmInstructionType instr = WI_NOP;
+    i32 alignment = type_get_alignment_log2(type);
+
+    if (is_basic && (type->Basic.flags & Basic_Flag_Pointer)) {
+        instr = WI_I32_LOAD;
+        alignment = 2;
+    }
+    else if (is_basic && ((type->Basic.flags & Basic_Flag_Integer)
+                       || (type->Basic.flags & Basic_Flag_Boolean)
+                       || (type->Basic.flags & Basic_Flag_Type_Index))) {
+        if      (load_size == 1) instr = WI_I32_LOAD_8_S;
+        else if (load_size == 2) instr = WI_I32_LOAD_16_S;
+        else if (load_size == 4) instr = WI_I32_LOAD;
+        else if (load_size == 8) instr = WI_I64_LOAD;
+
+        if (load_size < 4 && (type->Basic.flags & Basic_Flag_Unsigned)) instr += 1;
+    }
+    else if (is_basic && (type->Basic.flags & Basic_Flag_Float)) {
+        if      (load_size == 4) instr = WI_F32_LOAD;
+        else if (load_size == 8) instr = WI_F64_LOAD;
+    }
+    else if (is_basic && (type->Basic.flags & Basic_Flag_SIMD)) {
+        instr = WI_V128_LOAD;
+    }
+
+    WID(NULL, instr, ((WasmInstructionData) { alignment, offset }));
+
+    if (instr == WI_NOP) {
+        onyx_report_error((OnyxFilePos) { 0 }, Error_Critical,
+            "Failed to generate load instruction for type '%s'.",
+            type_get_name(type));
+    }
+
+    *pcode = code;
+}
+
+EMIT_FUNC(if, AstIfWhile* if_node) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    if (if_node->initialization != NULL) {
+        // bh_imap_put(&mod->local_map, (u64) if_node->local, local_allocate(mod->local_alloc, (AstTyped *) if_node->local));
+
+        forll (AstNode, stmt, if_node->initialization, next) {
+            emit_statement(mod, &code, stmt);
+        }
+    }
+
+    if (if_node->kind == Ast_Kind_Static_If) {
+        if (static_if_resolution(if_node)) {
+            if (if_node->true_stmt) emit_block(mod, &code, if_node->true_stmt, 1);
+        } else {
+            if (if_node->false_stmt) emit_block(mod, &code, if_node->false_stmt, 1);
+        }
+
+        *pcode = code;
+        return;
+    }
+
+    emit_expression(mod, &code, if_node->cond);
+
+    emit_enter_structured_block(mod, &code, SBT_Basic_If, if_node->token);
+    if (if_node->true_stmt) emit_block(mod, &code, if_node->true_stmt, 0);
+
+    if (if_node->false_stmt) {
+        WI(if_node->false_stmt->token, WI_ELSE);
+
+        if (if_node->false_stmt->kind == Ast_Kind_If) {
+            emit_if(mod, &code, (AstIfWhile *) if_node->false_stmt);
+        } else {
+            emit_block(mod, &code, if_node->false_stmt, 0);
+        }
+    }
+
+    emit_leave_structured_block(mod, &code);
+
+    *pcode = code;
+}
+
+EMIT_FUNC(while, AstIfWhile* while_node) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    if (while_node->initialization != NULL) {
+        forll (AstNode, stmt, while_node->initialization, next) {
+            emit_statement(mod, &code, stmt);
+        }
+    }
+
+    if (while_node->false_stmt == NULL) {
+        emit_enter_structured_block(mod, &code, SBT_Breakable_Block, while_node->token);
+        emit_enter_structured_block(mod, &code, SBT_Continue_Loop, while_node->token);
+
+        if (!while_node->bottom_test) {
+            emit_expression(mod, &code, while_node->cond);
+            WI(NULL, WI_I32_EQZ);
+            WID(NULL, WI_COND_JUMP, 0x01);
+        }
+
+        emit_block(mod, &code, while_node->true_stmt, 0);
+
+        if (while_node->bottom_test) {
+            emit_expression(mod, &code, while_node->cond);
+            WID(while_node->cond->token, WI_COND_JUMP, 0x00);
+
+        } else {
+            if (bh_arr_last(code).type != WI_JUMP)
+                WID(while_node->cond->token, WI_JUMP, 0x00);
+        }
+
+        emit_leave_structured_block(mod, &code);
+        emit_leave_structured_block(mod, &code);
+
+    } else {
+        emit_expression(mod, &code, while_node->cond);
+
+        emit_enter_structured_block(mod, &code, SBT_Breakable_If, while_node->token);
+        emit_enter_structured_block(mod, &code, SBT_Continue_Loop, while_node->token);
+
+        emit_block(mod, &code, while_node->true_stmt, 0);
+
+        emit_expression(mod, &code, while_node->cond);
+        WID(while_node->cond->token, WI_COND_JUMP, 0x00);
+
+        emit_leave_structured_block(mod, &code);
+        WI(while_node->false_stmt->token, WI_ELSE);
+
+        emit_block(mod, &code, while_node->false_stmt, 0);
+
+        emit_leave_structured_block(mod, &code);
+    }
+
+    *pcode = code;
+}
+
+EMIT_FUNC(for_range, AstFor* for_node, u64 iter_local) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    // NOTE: There are some aspects of the code below that rely on the
+    // low, high, and step members to be i32's. This restriction can be lifted,
+    // but it is important to change the code here.
+    //                                              -brendanfh   2020/09/04
+
+    // NOTE: This might not be a range literal
+    AstStructLiteral *range = (AstStructLiteral *) for_node->iter;
+    u64 offset = 0;
+
+    StructMember low_mem, high_mem, step_mem;
+    type_lookup_member(builtin_range_type_type, "low", &low_mem);
+    type_lookup_member(builtin_range_type_type, "high", &high_mem);
+    type_lookup_member(builtin_range_type_type, "step", &step_mem);
+    u64 low_local  = local_raw_allocate(mod->local_alloc, onyx_type_to_wasm_type(low_mem.type));
+    u64 high_local = local_raw_allocate(mod->local_alloc, onyx_type_to_wasm_type(high_mem.type));
+    u64 step_local = local_raw_allocate(mod->local_alloc, onyx_type_to_wasm_type(step_mem.type));
+
+    WIL(for_node->token, WI_LOCAL_SET, step_local);
+    WIL(for_node->token, WI_LOCAL_SET, high_local);
+    WIL(for_node->token, WI_LOCAL_TEE, low_local);
+    WIL(for_node->token, WI_LOCAL_SET, iter_local);
+
+    emit_enter_structured_block(mod, &code, SBT_Breakable_Block, for_node->token);
+    emit_enter_structured_block(mod, &code, SBT_Basic_Loop, for_node->token);
+    emit_enter_structured_block(mod, &code, SBT_Continue_Block, for_node->token);
+
+    if (range->kind == Ast_Kind_Struct_Literal && (range->args.values[2]->flags & Ast_Flag_Comptime) != 0) {
+        AstNumLit *step_value = (AstNumLit *) range->args.values[2];
+        assert(step_value->kind == Ast_Kind_NumLit);
+
+        if (step_value->value.l >= 0) {
+            WIL(for_node->token, WI_LOCAL_GET, iter_local);
+            WIL(for_node->token, WI_LOCAL_GET, high_local);
+            WI(for_node->token, WI_I32_GE_S);
+            WID(for_node->token, WI_COND_JUMP, 0x02);
+        } else {
+            WIL(for_node->token, WI_LOCAL_GET, iter_local);
+            WIL(for_node->token, WI_LOCAL_GET, high_local);
+            WI(for_node->token, WI_I32_LT_S);
+            WID(for_node->token, WI_COND_JUMP, 0x02);
+        }
+
+    } else {
+        WIL(for_node->token, WI_LOCAL_GET, step_local);
+        WID(for_node->token, WI_I32_CONST, 0);
+        WI(for_node->token, WI_I32_GE_S);
+        WID(for_node->token, WI_IF_START, 0x40);
+            WIL(for_node->token, WI_LOCAL_GET, iter_local);
+            WIL(for_node->token, WI_LOCAL_GET, high_local);
+            WI(for_node->token, WI_I32_GE_S);
+            WID(for_node->token, WI_COND_JUMP, 0x03);
+        WI(for_node->token, WI_ELSE);
+            WIL(for_node->token, WI_LOCAL_GET, iter_local);
+            WIL(for_node->token, WI_LOCAL_GET, high_local);
+            WI(for_node->token, WI_I32_LT_S);
+            WID(for_node->token, WI_COND_JUMP, 0x03);
+        WI(for_node->token, WI_IF_END);
+    }
+
+
+    emit_block(mod, &code, for_node->stmt, 0);
+
+    emit_leave_structured_block(mod, &code);
+
+    WIL(for_node->token, WI_LOCAL_GET, iter_local);
+    WIL(for_node->token, WI_LOCAL_GET, step_local);
+    WI(for_node->token, WI_I32_ADD);
+    WIL(for_node->token, WI_LOCAL_SET, iter_local);
+
+    if (bh_arr_last(code).type != WI_JUMP)
+        WID(for_node->token, WI_JUMP, 0x00);
+
+    emit_leave_structured_block(mod, &code);
+    emit_leave_structured_block(mod, &code);
+
+    local_raw_free(mod->local_alloc, onyx_type_to_wasm_type(low_mem.type));
+    local_raw_free(mod->local_alloc, onyx_type_to_wasm_type(high_mem.type));
+    local_raw_free(mod->local_alloc, onyx_type_to_wasm_type(step_mem.type));
+
+    *pcode = code;
+}
+
+EMIT_FUNC(for_array, AstFor* for_node, u64 iter_local) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    u64 end_ptr_local, ptr_local;
+    end_ptr_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+
+    if (for_node->by_pointer) {
+        ptr_local = iter_local;
+    } else {
+        ptr_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+    }
+
+    AstLocal* var = for_node->var;
+    b32 it_is_local = (b32) ((iter_local & LOCAL_IS_WASM) != 0);
+    u64 offset = 0;
+
+    u64 elem_size;
+    if (for_node->by_pointer) elem_size = type_size_of(var->type->Pointer.elem);
+    else                      elem_size = type_size_of(var->type);
+
+    WIL(for_node->token, WI_LOCAL_TEE, ptr_local);
+    WIL(for_node->token, WI_PTR_CONST, for_node->iter->type->Array.count * elem_size);
+    WI(for_node->token, WI_PTR_ADD);
+    WIL(for_node->token, WI_LOCAL_SET, end_ptr_local);
+
+    emit_enter_structured_block(mod, &code, SBT_Breakable_Block, for_node->token);
+    emit_enter_structured_block(mod, &code, SBT_Basic_Loop, for_node->token);
+    emit_enter_structured_block(mod, &code, SBT_Continue_Block, for_node->token);
+
+    WIL(for_node->token, WI_LOCAL_GET, ptr_local);
+    WIL(for_node->token, WI_LOCAL_GET, end_ptr_local);
+    WI(for_node->token, WI_PTR_GE);
+    WID(for_node->token, WI_COND_JUMP, 0x02);
+
+    if (!for_node->by_pointer) {
+        if (!it_is_local) emit_local_location(mod, &code, var, &offset);
+
+        WIL(for_node->token, WI_LOCAL_GET, ptr_local);
+        emit_load_instruction(mod, &code, var->type, 0);
+
+        if (!it_is_local) emit_store_instruction(mod, &code, var->type, offset);
+        else              WIL(for_node->token, WI_LOCAL_SET, iter_local);
+    }
+
+    emit_block(mod, &code, for_node->stmt, 0);
+
+    emit_leave_structured_block(mod, &code);
+
+    WIL(for_node->token, WI_LOCAL_GET, ptr_local);
+    WIL(for_node->token, WI_PTR_CONST, elem_size);
+    WI(for_node->token, WI_PTR_ADD);
+    WIL(for_node->token, WI_LOCAL_SET, ptr_local);
+
+    if (bh_arr_last(code).type != WI_JUMP)
+        WID(for_node->token, WI_JUMP, 0x00);
+
+    emit_leave_structured_block(mod, &code);
+    emit_leave_structured_block(mod, &code);
+
+    local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+    if (!for_node->by_pointer) local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+
+    *pcode = code;
+}
+
+EMIT_FUNC(for_slice, AstFor* for_node, u64 iter_local) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    u64 end_ptr_local, ptr_local;
+    end_ptr_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+
+    if (for_node->by_pointer) {
+        ptr_local = iter_local;
+    } else {
+        ptr_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+    }
+
+    AstLocal* var = for_node->var;
+    b32 it_is_local = (b32) ((iter_local & LOCAL_IS_WASM) != 0);
+    u64 offset = 0;
+
+    u64 elem_size;
+    if (for_node->by_pointer) elem_size = type_size_of(var->type->Pointer.elem);
+    else                      elem_size = type_size_of(var->type);
+
+    WIL(for_node->token, WI_LOCAL_SET, end_ptr_local);
+    WIL(for_node->token, WI_LOCAL_TEE, ptr_local);
+    WIL(for_node->token, WI_LOCAL_GET, end_ptr_local);
+    if (elem_size != 1) {
+        WID(for_node->token, WI_PTR_CONST, elem_size);
+        WI(for_node->token, WI_PTR_MUL);
+    }
+    WI(for_node->token, WI_PTR_ADD);
+    WIL(for_node->token, WI_LOCAL_SET, end_ptr_local);
+
+    emit_enter_structured_block(mod, &code, SBT_Breakable_Block, for_node->token);
+    emit_enter_structured_block(mod, &code, SBT_Basic_Loop, for_node->token);
+    emit_enter_structured_block(mod, &code, SBT_Continue_Block, for_node->token);
+
+    WIL(for_node->token, WI_LOCAL_GET, ptr_local);
+    WIL(for_node->token, WI_LOCAL_GET, end_ptr_local);
+    WI(for_node->token, WI_PTR_GE);
+    WID(for_node->token, WI_COND_JUMP, 0x02);
+
+    if (!for_node->by_pointer) {
+        if (!it_is_local) emit_local_location(mod, &code, var, &offset);
+
+        WIL(for_node->token, WI_LOCAL_GET, ptr_local);
+        emit_load_instruction(mod, &code, var->type, 0);
+
+        if (!it_is_local) emit_store_instruction(mod, &code, var->type, offset);
+        else              WIL(for_node->token, WI_LOCAL_SET, iter_local);
+    }
+
+    emit_block(mod, &code, for_node->stmt, 0);
+
+    emit_leave_structured_block(mod, &code);
+
+    WIL(for_node->token, WI_LOCAL_GET, ptr_local);
+    WIL(for_node->token, WI_PTR_CONST, elem_size);
+    WI(for_node->token, WI_PTR_ADD);
+    WIL(for_node->token, WI_LOCAL_SET, ptr_local);
+
+    if (bh_arr_last(code).type != WI_JUMP)
+        WID(for_node->token, WI_JUMP, 0x00);
+
+    emit_leave_structured_block(mod, &code);
+    emit_leave_structured_block(mod, &code);
+
+    local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+    if (!for_node->by_pointer) local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+
+    *pcode = code;
+}
+
+EMIT_FUNC(for_iterator, AstFor* for_node, u64 iter_local) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    // Allocate temporaries for iterator contents
+    u64 iterator_data_ptr    = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+    u64 iterator_next_func   = local_raw_allocate(mod->local_alloc, WASM_TYPE_FUNC);
+    u64 iterator_close_func  = local_raw_allocate(mod->local_alloc, WASM_TYPE_FUNC);
+    u64 iterator_remove_func = local_raw_allocate(mod->local_alloc, WASM_TYPE_FUNC);
+    u64 iterator_done_bool   = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
+    WIL(for_node->token, WI_LOCAL_SET, iterator_remove_func);
+    WIL(for_node->token, WI_LOCAL_SET, iterator_close_func);
+    WIL(for_node->token, WI_LOCAL_SET, iterator_next_func);
+    WIL(for_node->token, WI_LOCAL_SET, iterator_data_ptr);
+
+
+    {
+        //
+        // This pushes an entry onto the stack of for loops that have
+        // are iterator that can have a '#remove' directive in them.
+        ForRemoveInfo remove_info;
+        remove_info.iterator_data_ptr = iterator_data_ptr;
+        remove_info.iterator_remove_func = iterator_remove_func;
+
+        TypeWithOffset remove_func_type;
+        type_linear_member_lookup(for_node->iter->type, 3, &remove_func_type);
+        remove_info.remove_func_type_idx = generate_type_idx(mod, remove_func_type.type);
+
+        bh_arr_push(mod->for_remove_info, remove_info);
+    }
+
+    AstLocal* var = for_node->var;
+    b32 it_is_local = (b32) ((iter_local & LOCAL_IS_WASM) != 0);
+    u64 offset = 0;
+
+    // Enter a deferred statement for the auto-close
+    emit_enter_structured_block(mod, &code, SBT_Basic_Block, for_node->token);
+
+    if (!for_node->no_close) {
+        TypeWithOffset close_func_type;
+        type_linear_member_lookup(for_node->iter->type, 2, &close_func_type);
+        i32 close_type_idx = generate_type_idx(mod, close_func_type.type);
+
+        WasmInstruction* close_instructions = bh_alloc_array(global_heap_allocator, WasmInstruction, 8);
+        close_instructions[0] = (WasmInstruction) { WI_LOCAL_GET,     { .l = iterator_close_func } };
+        close_instructions[1] = (WasmInstruction) { WI_I32_CONST,     { .l = mod->null_proc_func_idx } };
+        close_instructions[2] = (WasmInstruction) { WI_I32_NE,        { .l = 0x00 } };
+        close_instructions[3] = (WasmInstruction) { WI_IF_START,      { .l = 0x40 } };
+        close_instructions[4] = (WasmInstruction) { WI_LOCAL_GET,     { .l = iterator_data_ptr } };
+        close_instructions[5] = (WasmInstruction) { WI_LOCAL_GET,     { .l = iterator_close_func } };
+        close_instructions[6] = (WasmInstruction) { WI_CALL_INDIRECT, { .l = close_type_idx } };
+        close_instructions[7] = (WasmInstruction) { WI_IF_END,        { .l = 0x00 } };
+
+        emit_defer_code(mod, &code, close_instructions, 8);
+    }
+
+    emit_enter_structured_block(mod, &code, SBT_Breakable_Block, for_node->token);
+    emit_enter_structured_block(mod, &code, SBT_Continue_Loop, for_node->token);
+
+    if (!it_is_local) emit_local_location(mod, &code, var, &offset);
+
+    {
+        WIL(for_node->token, WI_LOCAL_GET, iterator_data_ptr);
+        WIL(for_node->token, WI_LOCAL_GET, iterator_next_func);
+
+        // CLEANUP: Calling a function is way too f-ing complicated. FACTOR IT!!
+        u64 stack_top_idx = bh_imap_get(&mod->index_map, (u64) &builtin_stack_top);
+
+        TypeWithOffset next_func_type;
+        type_linear_member_lookup(for_node->iter->type, 1, &next_func_type);
+        Type* return_type = next_func_type.type->Function.return_type;
+
+        u32 return_size = type_size_of(return_type);
+        u32 return_align = type_alignment_of(return_type);
+        bh_align(return_size, return_align);
+
+        u64 reserve_size = return_size;
+        bh_align(reserve_size, 16);
+
+        WID(for_node->token, WI_GLOBAL_GET, stack_top_idx);
+        WID(for_node->token, WI_PTR_CONST, reserve_size);
+        WI(for_node->token, WI_PTR_ADD);
+        WID(for_node->token, WI_GLOBAL_SET, stack_top_idx);
+
+        i32 type_idx = generate_type_idx(mod, next_func_type.type);
+        WID(for_node->token, WI_CALL_INDIRECT, ((WasmInstructionData) { type_idx, 0x00 }));
+
+        WID(for_node->token, WI_GLOBAL_GET, stack_top_idx);
+        WID(for_node->token, WI_PTR_CONST, reserve_size);
+        WI(for_node->token, WI_PTR_SUB);
+        WID(for_node->token, WI_GLOBAL_SET, stack_top_idx);
+
+        WID(for_node->token, WI_GLOBAL_GET, stack_top_idx);
+        emit_load_instruction(mod, &code, return_type, reserve_size - return_size);
+    }
+
+    WIL(for_node->token, WI_LOCAL_SET, iterator_done_bool);
+
+    if (!it_is_local) emit_store_instruction(mod, &code, var->type, offset);
+    else              WIL(for_node->token, WI_LOCAL_SET, iter_local);
+
+    WIL(for_node->token, WI_LOCAL_GET, iterator_done_bool);
+    WI(for_node->token, WI_I32_EQZ);
+    WID(for_node->token, WI_COND_JUMP, 0x01);
+
+    emit_block(mod, &code, for_node->stmt, 0);
+    WID(for_node->token, WI_JUMP, 0x00);
+
+    emit_leave_structured_block(mod, &code);
+    emit_leave_structured_block(mod, &code);
+
+    emit_deferred_stmts(mod, &code);
+    emit_leave_structured_block(mod, &code);
+
+    bh_arr_pop(mod->for_remove_info);
+
+    local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+    local_raw_free(mod->local_alloc, WASM_TYPE_FUNC);
+    local_raw_free(mod->local_alloc, WASM_TYPE_FUNC);
+    local_raw_free(mod->local_alloc, WASM_TYPE_INT32);
+    *pcode = code;
+}
+
+EMIT_FUNC(for, AstFor* for_node) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    AstLocal* var = for_node->var;
+    u64 iter_local = local_allocate(mod->local_alloc, (AstTyped *) var);
+    bh_imap_put(&mod->local_map, (u64) var, iter_local);
+
+    debug_enter_symbol_frame(mod);
+    debug_introduce_symbol(mod, var->token,
+        local_is_wasm_local((AstTyped *) var) ? DSL_REGISTER : DSL_STACK,
+        iter_local, var->type);
+
+    emit_expression(mod, &code, for_node->iter);
+
+    switch (for_node->loop_type) {
+        case For_Loop_Range:    emit_for_range(mod, &code, for_node, iter_local); break;
+        case For_Loop_Array:    emit_for_array(mod, &code, for_node, iter_local); break;
+        // NOTE: A dynamic array is just a slice with a capacity and allocator on the end.
+        // Just dropping the extra fields will mean we can just use the slice implementation.
+        //                                                  - brendanfh   2020/09/04
+        //                                                  - brendanfh   2021/04/13
+        case For_Loop_DynArr:   WI(for_node->token, WI_DROP); WI(for_node->token, WI_DROP); WI(for_node->token, WI_DROP);
+        case For_Loop_Slice:    emit_for_slice(mod, &code, for_node, iter_local); break;
+        case For_Loop_Iterator: emit_for_iterator(mod, &code, for_node, iter_local); break;
+        default: onyx_report_error(for_node->token->pos, Error_Critical, "Invalid for loop type. You should probably not be seeing this...");
+    }
+
+    local_free(mod->local_alloc, (AstTyped *) var);
+    debug_leave_symbol_frame(mod);
+
+    *pcode = code;
+}
+
+EMIT_FUNC(switch, AstSwitch* switch_node) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    bh_imap block_map;
+    bh_imap_init(&block_map, global_heap_allocator, bh_arr_length(switch_node->cases));
+
+    if (switch_node->initialization != NULL) {
+        forll (AstNode, stmt, switch_node->initialization, next) {
+            emit_statement(mod, &code, stmt);
+        }
+    }
+
+    emit_enter_structured_block(mod, &code, SBT_Breakable_Block, switch_node->token);
+
+    u64 block_num = 0;
+    bh_arr_each(AstSwitchCase *, sc, switch_node->cases) {
+        if (bh_imap_has(&block_map, (u64) (*sc)->block)) continue;
+
+        emit_enter_structured_block(mod, &code, SBT_Fallthrough_Block, (*sc)->block->token);
+
+        bh_imap_put(&block_map, (u64) (*sc)->block, block_num);
+        block_num++;
+    }
+
+    switch (switch_node->switch_kind) {
+        case Switch_Kind_Integer: {
+            u64 count = switch_node->max_case + 1 - switch_node->min_case;
+            BranchTable* bt = bh_alloc(mod->extended_instr_alloc, sizeof(BranchTable) + sizeof(u32) * count);
+            bt->count = count;
+            bt->default_case = block_num;
+            fori (i, 0, bt->count) bt->cases[i] = bt->default_case;
+
+            bh_arr_each(bh__imap_entry, sc, switch_node->case_map.entries) {
+                bt->cases[sc->key - switch_node->min_case] = bh_imap_get(&block_map, (u64) sc->value);
+            }
+
+            // NOTE: We enter a new block here in order to setup the correct
+            // indicies for the jump targets in the branch table. For example,
+            //
+            // <expr>
+            // jump_table
+            // label0:
+            // ...
+            // label1:
+            // ...
+            //
+            // If we didn't enter a new block, then jumping to label 0, would jump
+            // to the second block, and so on.
+            WID(switch_node->expr->token, WI_BLOCK_START, 0x40);
+            emit_expression(mod, &code, switch_node->expr);
+            if (switch_node->min_case != 0) {
+                WID(switch_node->expr->token, WI_I32_CONST, switch_node->min_case);
+                WI(switch_node->expr->token, WI_I32_SUB);
+            }
+            WIP(switch_node->expr->token, WI_JUMP_TABLE, bt);
+            WI(switch_node->expr->token, WI_BLOCK_END);
+            break;
+        }
+
+        case Switch_Kind_Use_Equals: {
+            WID(switch_node->expr->token, WI_BLOCK_START, 0x40);
+
+            bh_arr_each(CaseToBlock, ctb, switch_node->case_exprs) {
+                emit_expression(mod, &code, (AstTyped *) ctb->comparison);
+
+                u64 bn = bh_imap_get(&block_map, (u64) ctb->block);
+                WID(switch_node->expr->token, WI_IF_START, 0x40);
+                WID(switch_node->expr->token, WI_JUMP, bn + 1);
+                WI(switch_node->expr->token, WI_IF_END);
+            }
+
+            WID(switch_node->expr->token, WI_JUMP, block_num);
+            WI(switch_node->expr->token, WI_BLOCK_END);
+            break;
+        }
+    }
+
+    bh_arr_each(AstSwitchCase *, psc, switch_node->cases) {
+        AstSwitchCase *sc = *psc;
+        if (bh_imap_get(&block_map, (u64) sc->block) == 0xdeadbeef) continue;
+
+        u64 bn = bh_imap_get(&block_map, (u64) sc->block);
+
+        // Maybe the Symbol Frame idea should be controlled as a block_flag?
+        debug_enter_symbol_frame(mod);
+        emit_block(mod, &code, sc->block, 0);
+        debug_leave_symbol_frame(mod);
+
+        if (bh_arr_last(code).type != WI_JUMP)
+            WID(NULL, WI_JUMP, block_num - bn);
+
+        emit_leave_structured_block(mod, &code);
+
+        bh_imap_put(&block_map, (u64) sc->block, 0xdeadbeef);
+    }
+
+    if (switch_node->default_case != NULL) {
+        emit_block(mod, &code, switch_node->default_case, 0);
+    }
+
+    emit_leave_structured_block(mod, &code);
+
+    bh_imap_free(&block_map);
+    *pcode = code;
+}
+
+EMIT_FUNC(defer, AstDefer* defer) {
+    bh_arr_push(mod->deferred_stmts, ((DeferredStmt) {
+        .type = Deferred_Stmt_Node,
+        .depth = bh_arr_length(mod->structured_jump_target),
+        .defer_node= defer,
+        .stmt = defer->stmt,
+    }));
+}
+
+EMIT_FUNC(defer_code, WasmInstruction* deferred_code, u32 code_count) {
+    bh_arr_push(mod->deferred_stmts, ((DeferredStmt) {
+        .type = Deferred_Stmt_Code,
+        .depth = bh_arr_length(mod->structured_jump_target),
+        .defer_node= NULL,
+        .instructions = deferred_code,
+        .instruction_count = code_count,
+    }));
+}
+
+EMIT_FUNC(deferred_stmt, DeferredStmt deferred_stmt) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    if (deferred_stmt.defer_node) {
+        WI(deferred_stmt.defer_node->token, WI_NOP);
+    }
+
+    switch (deferred_stmt.type) {
+        case Deferred_Stmt_Node: emit_statement(mod, &code, deferred_stmt.stmt); break;
+        case Deferred_Stmt_Code: {
+            fori (i, 0, deferred_stmt.instruction_count) {
+                WIR(NULL, deferred_stmt.instructions[i]);
+            }
+            break;
+        }
+    }
+
+    *pcode = code;
+}
+
+EMIT_FUNC_NO_ARGS(deferred_stmts) {
+    if (bh_arr_length(mod->deferred_stmts) <= 0) return;
+
+    u64 depth = bh_arr_length(mod->structured_jump_target);
+
+    while (bh_arr_length(mod->deferred_stmts) > 0 && bh_arr_last(mod->deferred_stmts).depth >= depth) {
+        DeferredStmt stmt = bh_arr_pop(mod->deferred_stmts);
+        emit_deferred_stmt(mod, pcode, stmt);
+    }
+}
+
+EMIT_FUNC(remove_directive, AstDirectiveRemove* remove) {
+    assert(bh_arr_length(mod->for_remove_info) > 0);
+
+    bh_arr(WasmInstruction) code = *pcode;
+
+    ForRemoveInfo remove_info = bh_arr_last(mod->for_remove_info);
+
+    WIL(remove->token, WI_LOCAL_GET, remove_info.iterator_remove_func);
+    WIL(remove->token, WI_I32_CONST, mod->null_proc_func_idx);
+    WI(remove->token, WI_I32_NE);
+    WID(remove->token, WI_IF_START, 0x40);
+    WIL(remove->token, WI_LOCAL_GET, remove_info.iterator_data_ptr);
+    WIL(remove->token, WI_LOCAL_GET, remove_info.iterator_remove_func);
+    WIL(remove->token, WI_CALL_INDIRECT, remove_info.remove_func_type_idx);
+    WI(remove->token, WI_IF_END);
+
+    *pcode = code;
+}
+
+// NOTE: These need to be in the same order as
+// the OnyxBinaryOp enum
+static const WasmInstructionType binop_map[][4] = {
+    //           I32           I64           F32         F64
+    /* ADD */  { WI_I32_ADD,   WI_I64_ADD,   WI_F32_ADD, WI_F64_ADD },
+    /* SUB */  { WI_I32_SUB,   WI_I64_SUB,   WI_F32_SUB, WI_F64_SUB },
+    /* MUL */  { WI_I32_MUL,   WI_I64_MUL,   WI_F32_MUL, WI_F64_MUL },
+    /* DIV */  { WI_I32_DIV_S, WI_I64_DIV_S, WI_F32_DIV, WI_F64_DIV },
+    /* REM */  { WI_I32_REM_S, WI_I64_REM_S, WI_NOP,     WI_NOP     },
+
+    /* EQ  */  { WI_I32_EQ,    WI_I64_EQ,    WI_F32_EQ,  WI_F64_EQ },
+    /* NEQ */  { WI_I32_NE,    WI_I64_NE,    WI_F32_NE , WI_F64_NE },
+    /* LT  */  { WI_I32_LT_S,  WI_I64_LT_S,  WI_F32_LT,  WI_F64_LT },
+    /* LTE */  { WI_I32_LE_S,  WI_I64_LE_S,  WI_F32_LE,  WI_F64_LE },
+    /* GT  */  { WI_I32_GT_S,  WI_I64_GT_S,  WI_F32_GT,  WI_F64_GT },
+    /* GTE */  { WI_I32_GE_S,  WI_I64_GE_S,  WI_F32_GE,  WI_F64_GE },
+
+    /* AND */  { WI_I32_AND,   WI_I64_AND,   WI_NOP,     WI_NOP },
+    /* OR  */  { WI_I32_OR,    WI_I64_OR,    WI_NOP,     WI_NOP },
+    /* XOR */  { WI_I32_XOR,   WI_I64_XOR,   WI_NOP,     WI_NOP },
+    /* SHL */  { WI_I32_SHL,   WI_I64_SHL,   WI_NOP,     WI_NOP },
+    /* SHR */  { WI_I32_SHR_U, WI_I64_SHR_U, WI_NOP,     WI_NOP },
+    /* SAR */  { WI_I32_SHR_S, WI_I64_SHR_S, WI_NOP,     WI_NOP },
+
+    /* BAND */ { WI_I32_AND,   WI_I64_AND,   WI_NOP,     WI_NOP },
+    /* BOR  */ { WI_I32_OR,    WI_I64_OR,    WI_NOP,     WI_NOP },
+};
+
+EMIT_FUNC(binop, AstBinaryOp* binop) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    if (binop_is_assignment(binop->operation)) {
+        emit_assignment(mod, &code, binop);
+        *pcode = code;
+        return;
+    }
+
+    b32 is_sign_significant = 0;
+    switch (binop->operation) {
+        case Binary_Op_Divide:  case Binary_Op_Modulus:
+        case Binary_Op_Less:    case Binary_Op_Less_Equal:
+        case Binary_Op_Greater: case Binary_Op_Greater_Equal:
+            is_sign_significant = 1;
+    }
+
+    WasmType operator_type = onyx_type_to_wasm_type(binop->left->type);
+    i32 optype = 0;
+    if      (operator_type == WASM_TYPE_INT32)   optype = 0;
+    else if (operator_type == WASM_TYPE_INT64)   optype = 1;
+    else if (operator_type == WASM_TYPE_FLOAT32) optype = 2;
+    else if (operator_type == WASM_TYPE_FLOAT64) optype = 3;
+
+    WasmInstructionType binop_instr = binop_map[(i32) binop->operation][optype];
+
+    assert(binop_instr != WI_NOP);
+
+    // NOTE: Use unsigned variant if needed
+    // Unsigned instructions are always right after
+    // the signed equivalent
+    if (is_sign_significant) {
+        if (binop->left->type->Basic.flags & Basic_Flag_Unsigned) {
+            binop_instr = (WasmInstructionType) ((i32) binop_instr + 1);
+        }
+    }
+
+    emit_expression(mod, &code, binop->left);
+    emit_expression(mod, &code, binop->right);
+
+    WI(binop->token, binop_instr);
+
+    *pcode = code;
+}
+
+EMIT_FUNC(unaryop, AstUnaryOp* unop) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    switch (unop->operation) {
+        case Unary_Op_Negate: {
+            TypeBasic* type = &unop->type->Basic;
+
+            if (type->kind == Basic_Kind_I32
+                    || type->kind == Basic_Kind_I16
+                    || type->kind == Basic_Kind_I8) {
+                WID(unop->token, WI_I32_CONST, 0x00);
+                emit_expression(mod, &code, unop->expr);
+                WI(unop->token, WI_I32_SUB);
+
+            }
+            else if (type->kind == Basic_Kind_I64) {
+                WID(unop->token, WI_I64_CONST, 0x00);
+                emit_expression(mod, &code, unop->expr);
+                WI(unop->token, WI_I64_SUB);
+
+            }
+            else {
+                emit_expression(mod, &code, unop->expr);
+
+                if (type->kind == Basic_Kind_F32) WI(unop->token, WI_F32_NEG);
+                if (type->kind == Basic_Kind_F64) WI(unop->token, WI_F64_NEG);
+            }
+
+            break;
+        }
+
+        case Unary_Op_Not:
+            emit_expression(mod, &code, unop->expr);
+
+            WI(unop->token, WI_I32_EQZ);
+            break;
+
+        case Unary_Op_Bitwise_Not: {
+            emit_expression(mod, &code, unop->expr);
+
+            TypeBasic* type = &unop->type->Basic;
+
+            if (type->kind == Basic_Kind_I8 || type->kind == Basic_Kind_U8) {
+                WID(unop->token, WI_I32_CONST, 0xff);
+                WI(unop->token, WI_I32_XOR);
+            }
+            else if (type->kind == Basic_Kind_I16 || type->kind == Basic_Kind_U16) {
+                WID(unop->token, WI_I32_CONST, 0xffff);
+                WI(unop->token, WI_I32_XOR);
+            }
+            else if (type->kind == Basic_Kind_I32 || type->kind == Basic_Kind_U32) {
+                WID(unop->token, WI_I32_CONST, 0xffffffff);
+                WI(unop->token, WI_I32_XOR);
+            }
+            else if (type->kind == Basic_Kind_I64 || type->kind == Basic_Kind_U64) {
+                WIL(unop->token, WI_I64_CONST, 0xffffffffffffffff);
+                WI(unop->token, WI_I64_XOR);
+            }
+
+            break;
+        }
+
+        case Unary_Op_Auto_Cast:
+        case Unary_Op_Cast: emit_cast(mod, &code, unop); break;
+    }
+
+    *pcode = code;
+}
+
+// Calling a procedure in Onyx.
+//
+// This documentation should be placed elsewhere, but for right now I'm going to write it in the relevant
+// piece of code. Calling a procedure is relatively simple, at least compared to other calling conventions
+// out there, mostly due the fact that this is WebAssembly, where registers are "infinite" and there's no
+// really need to use stack canaries for security.
+//
+// The biggest piece to understand is how the stack gets laid out for the called procedure. To be confusing,
+// there are two stacks at play: the WASM expression stack, and the linear memory stack. Here is the general
+// lay out for calling a procedure with the following signature.
+//
+//    foo :: (x: i32, y: str, z: [..] i32, va: ..i32) -> (i32, i32)
+//
+// WASM stack: top is last pushed
+//
+//    vararg count
+//    vararg pointer to variadic arguments in the linear memory
+//    pointer to struct-like arguments (z)
+//    simple structs/primitives (y, x)
+//
+// Linear memory stack:
+//
+//     ... | struct-like arguments (z) | variadic arguments (va) | return space | ...
+//
+// The interesting part from above is the fact that 'y' gets passed on the WASM stack, not the linear memory
+// stack, even though a 'str' in Onyx is 2-component structure. This is because so-called "simple" structures,
+// i.e. structures that are completely flat, with no sub-structures, are passed as multiple primitives. I do
+// this because many times for interoperability, it is nicer to get two primitive values for the pointer and
+// count of a slice, instead of a pointer.
+EMIT_FUNC(call, AstCall* call) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    u64 stack_top_idx = bh_imap_get(&mod->index_map, (u64) &builtin_stack_top);
+    u64 stack_top_store_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+
+    OnyxToken* call_token = call->token;
+
+    // Because it would be inefficient to increment and decrement the global stack pointer for every argument,
+    // a simple set of instructions increments it once to the size it will need to be. However, because it is
+    // impossible to know what size the reserved memory will be, a location patch is taken in order to fill it
+    // in later.
+    u32 reserve_space_patch = bh_arr_length(code);
+    WID(call_token, WI_GLOBAL_GET, stack_top_idx);
+    WIL(call_token, WI_LOCAL_TEE, stack_top_store_local);
+    WID(call_token, WI_PTR_CONST, 0);                           // This will be filled in later.
+    WI(call_token, WI_PTR_ADD);
+    WID(call_token, WI_GLOBAL_SET, stack_top_idx);
+
+    u32 reserve_size  = 0;
+    u32 vararg_count  = 0;
+    i32 vararg_offset = -1;
+
+    u32* vararg_any_offsets=NULL;
+    u32* vararg_any_types=NULL;
+    if (call->va_kind == VA_Kind_Any) {
+        vararg_any_offsets = bh_alloc_array(global_scratch_allocator, u32, bh_arr_length(call->args.values));
+        vararg_any_types   = bh_alloc_array(global_scratch_allocator, u32, bh_arr_length(call->args.values));
+    }
+
+    bh_arr_each(AstTyped *, parg, call->args.values) {
+        AstArgument* arg = (AstArgument *) *parg;
+        if (arg->is_baked) continue;
+
+        b32 place_on_stack = 0;
+
+        if (type_get_param_pass(arg->value->type) == Param_Pass_By_Implicit_Pointer) {
+            // This arguments needs to be written to the stack because it is not a simple structure.
+            place_on_stack = 1;
+        }
+
+        if (arg->va_kind != VA_Kind_Not_VA) {
+            // This is a variadic argument and needs to be written to the stack. If the starting
+            // location of the vararg array hasn't been noted, note it.
+            if (vararg_offset < 0) vararg_offset = reserve_size;
+
+            place_on_stack = 1;
+            vararg_count += 1;
+        }
+
+        if (arg->pass_as_any) {
+            place_on_stack = 1;
+        }
+
+        if (place_on_stack) WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
+
+        emit_expression(mod, &code, arg->value);
+
+        if (place_on_stack) {
+            emit_store_instruction(mod, &code, arg->value->type, reserve_size);
+
+            if (arg->va_kind == VA_Kind_Not_VA) {
+                // Non-variadic arguments on the stack need a pointer to them placed on the WASM stack.
+                WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
+                WID(call_token, WI_PTR_CONST, reserve_size);
+                WI(call_token, WI_PTR_ADD);
+            }
+
+            if (arg->va_kind == VA_Kind_Any) {
+                vararg_any_offsets[vararg_count - 1] = reserve_size;
+                vararg_any_types[vararg_count - 1] = arg->value->type->id;
+            }
+
+            if (arg->pass_as_any) {
+                WIL(call_token, WI_I32_CONST, arg->value->type->id);
+            }
+
+            reserve_size += type_size_of(arg->value->type);
+        }
+    }
+
+    switch (call->va_kind) {
+        case VA_Kind_Any: {
+            vararg_offset = reserve_size;
+
+            i32 any_size = type_size_of(type_build_from_ast(context.ast_alloc, builtin_any_type));
+
+            fori (i, 0, vararg_count) {
+                WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
+                WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
+                WID(call_token, WI_PTR_CONST, vararg_any_offsets[i]);
+                WI(call_token, WI_PTR_ADD);
+                emit_store_instruction(mod, &code, &basic_types[Basic_Kind_Rawptr], vararg_offset + i * any_size);
+
+                WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
+                WID(call_token, WI_I32_CONST, vararg_any_types[i]);
+                emit_store_instruction(mod, &code, &basic_types[Basic_Kind_Type_Index], vararg_offset + i * any_size + POINTER_SIZE);
+
+                reserve_size += any_size;
+            }
+
+            // fallthrough
+        }
+
+        case VA_Kind_Typed: {
+            WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
+            if (vararg_offset > 0) {
+                WID(call_token, WI_PTR_CONST, vararg_offset);
+                WI(call_token, WI_PTR_ADD);
+            }
+            WID(call_token, WI_I32_CONST, vararg_count);
+            break;
+        }
+
+        case VA_Kind_Untyped: {
+            WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
+            WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
+            if (vararg_offset > 0) {
+                WID(call_token, WI_PTR_CONST, vararg_offset);
+                WI(call_token, WI_PTR_ADD);
+            }
+            emit_store_instruction(mod, &code, &basic_types[Basic_Kind_Rawptr], reserve_size);
+
+            // NOTE: There may be 4 uninitialized bytes here, because pointers are only 4 bytes in WASM.
+
+            WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
+            WID(call_token, WI_I32_CONST, vararg_count);
+            emit_store_instruction(mod, &code, &basic_types[Basic_Kind_I32], reserve_size + POINTER_SIZE);
+
+            WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
+            if (reserve_size > 0) {
+                WID(call_token, WI_PTR_CONST, reserve_size);
+                WI(call_token, WI_PTR_ADD);
+            }
+
+            reserve_size += 4 + POINTER_SIZE;
+            break;
+        }
+    }
+
+    CallingConvention cc = type_function_get_cc(call->callee->type);
+    assert(cc != CC_Undefined);
+
+    Type* return_type = call->callee->type->Function.return_type;
+    u32 return_size = type_size_of(return_type);
+    assert(return_size % type_alignment_of(return_type) == 0);
+
+    if (cc == CC_Return_Stack) reserve_size += return_size;
+
+    if (call->callee->kind == Ast_Kind_Function) {
+        i32 func_idx = (i32) bh_imap_get(&mod->index_map, (u64) call->callee);
+        WIL(NULL, WI_CALL, func_idx);
+
+    } else {
+        emit_expression(mod, &code, call->callee);
+
+        i32 type_idx = generate_type_idx(mod, call->callee->type);
+        WID(NULL, WI_CALL_INDIRECT, ((WasmInstructionData) { type_idx, 0x00 }));
+    }
+
+    if (reserve_size > 0) {
+        WIL(call_token, WI_LOCAL_GET,  stack_top_store_local);
+        WID(call_token, WI_GLOBAL_SET, stack_top_idx);
+
+        bh_align(reserve_size, 16);
+        code[reserve_space_patch + 2].data.l = reserve_size;
+
+    } else {
+        fori (i, 0, 5) code[reserve_space_patch + i].type = WI_NOP;
+    }
+
+    if (cc == CC_Return_Stack) {
+        WID(call_token, WI_GLOBAL_GET, stack_top_idx);
+        emit_load_instruction(mod, &code, return_type, reserve_size - return_size);
+    }
+
+    local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+    *pcode = code;
+}
+
+// BUG: This implementation assumes that the host system C's implementation is using
+// little endian integers.
+#define SIMD_INT_CONST_INTRINSIC(type, count) { \
+        type* byte_buffer = bh_alloc(mod->extended_instr_alloc, 16); \
+        bh_arr(AstArgument *) arg_arr = (bh_arr(AstArgument *)) call->args.values; \
+        fori (i, 0, count) { \
+            if (arg_arr[i]->value->kind != Ast_Kind_NumLit) { \
+                onyx_report_error(arg_arr[i]->token->pos, Error_Critical, \
+                        "SIMD constants expect compile time constants as parameters. The %d%s parameter was not.", \
+                        i, bh_num_suffix(i)); \
+                *pcode = code; \
+                return; \
+            } \
+            byte_buffer[i] = (type) ((AstNumLit *) arg_arr[i]->value)->value.l; \
+        } \
+        WIP(call->token, WI_V128_CONST, byte_buffer); \
+    }
+
+#define SIMD_EXTRACT_LANE_INSTR(instr, arg_arr) \
+    emit_expression(mod, &code, arg_arr[0]->value);\
+    if (arg_arr[1]->value->kind != Ast_Kind_NumLit) { \
+        onyx_report_error(arg_arr[1]->token->pos, Error_Critical, "SIMD lane instructions expect a compile time lane number."); \
+        *pcode = code; \
+        return; \
+    } \
+    WID(call->token, instr, (u8) ((AstNumLit *) arg_arr[1]->value)->value.i);
+
+#define SIMD_REPLACE_LANE_INSTR(instr, arg_arr) { \
+    emit_expression(mod, &code, arg_arr[0]->value);\
+    if (arg_arr[1]->value->kind != Ast_Kind_NumLit) { \
+        onyx_report_error(arg_arr[1]->token->pos, Error_Critical, "SIMD lane instructions expect a compile time lane number."); \
+        *pcode = code; \
+        return; \
+    } \
+    u8 lane = (u8) ((AstNumLit *) arg_arr[1]->value)->value.i; \
+    emit_expression(mod, &code, arg_arr[2]->value); \
+    WID(call->token, instr, lane); \
+}
+
+
+EMIT_FUNC(intrinsic_call, AstCall* call) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    b32 place_arguments_normally = 1;
+
+    switch (call->intrinsic) {
+        case ONYX_INTRINSIC_V128_CONST:
+        case ONYX_INTRINSIC_I8X16_CONST: case ONYX_INTRINSIC_I16X8_CONST:
+        case ONYX_INTRINSIC_I32X4_CONST: case ONYX_INTRINSIC_I64X2_CONST:
+        case ONYX_INTRINSIC_F32X4_CONST: case ONYX_INTRINSIC_F64X2_CONST:
+        case ONYX_INTRINSIC_I8X16_EXTRACT_LANE_S: case ONYX_INTRINSIC_I8X16_EXTRACT_LANE_U:
+        case ONYX_INTRINSIC_I16X8_EXTRACT_LANE_S: case ONYX_INTRINSIC_I16X8_EXTRACT_LANE_U:
+        case ONYX_INTRINSIC_I32X4_EXTRACT_LANE:   case ONYX_INTRINSIC_I64X2_EXTRACT_LANE:
+        case ONYX_INTRINSIC_F32X4_EXTRACT_LANE:   case ONYX_INTRINSIC_F64X2_EXTRACT_LANE:
+        case ONYX_INTRINSIC_I8X16_REPLACE_LANE:   case ONYX_INTRINSIC_I16X8_REPLACE_LANE:
+        case ONYX_INTRINSIC_I32X4_REPLACE_LANE:   case ONYX_INTRINSIC_I64X2_REPLACE_LANE:
+        case ONYX_INTRINSIC_F32X4_REPLACE_LANE:   case ONYX_INTRINSIC_F64X2_REPLACE_LANE:
+        case ONYX_INTRINSIC_I8X16_SHUFFLE:
+            place_arguments_normally = 0;
+
+        default: break;
+    }
+
+    if (place_arguments_normally) {
+        bh_arr_each(AstTyped *, arg, call->args.values) {
+            emit_expression(mod, &code, *arg);
+        }
+    }
+
+    switch (call->intrinsic) {
+        case ONYX_INTRINSIC_MEMORY_SIZE:  WID(call->token, WI_MEMORY_SIZE, 0x00); break;
+        case ONYX_INTRINSIC_MEMORY_GROW:  WID(call->token, WI_MEMORY_GROW, 0x00); break;
+        case ONYX_INTRINSIC_MEMORY_COPY:
+            if (context.options->use_post_mvp_features) {
+                WIL(call->token, WI_MEMORY_COPY, 0x00);
+            } else {
+                emit_intrinsic_memory_copy(mod, &code);
+            }
+            break;
+        case ONYX_INTRINSIC_MEMORY_FILL:
+            if (context.options->use_post_mvp_features) {
+                WIL(call->token, WI_MEMORY_FILL, 0x00);
+            } else {
+                emit_intrinsic_memory_fill(mod, &code);
+            }
+            break;
+
+        case ONYX_INTRINSIC_INITIALIZE: {
+            Type* type_to_initialize = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
+            emit_initialize_type(mod, &code, type_to_initialize, call->token);
+            break;
+        }
+
+        case ONYX_INTRINSIC_I32_CLZ:      WI(call->token, WI_I32_CLZ); break;
+        case ONYX_INTRINSIC_I32_CTZ:      WI(call->token, WI_I32_CTZ); break;
+        case ONYX_INTRINSIC_I32_POPCNT:   WI(call->token, WI_I32_POPCNT); break;
+        case ONYX_INTRINSIC_I32_AND:      WI(call->token, WI_I32_AND); break;
+        case ONYX_INTRINSIC_I32_OR:       WI(call->token, WI_I32_OR); break;
+        case ONYX_INTRINSIC_I32_XOR:      WI(call->token, WI_I32_XOR); break;
+        case ONYX_INTRINSIC_I32_SHL:      WI(call->token, WI_I32_SHL); break;
+        case ONYX_INTRINSIC_I32_SLR:      WI(call->token, WI_I32_SHR_U); break;
+        case ONYX_INTRINSIC_I32_SAR:      WI(call->token, WI_I32_SHR_S); break;
+        case ONYX_INTRINSIC_I32_ROTL:     WI(call->token, WI_I32_ROTL); break;
+        case ONYX_INTRINSIC_I32_ROTR:     WI(call->token, WI_I32_ROTR); break;
+
+        case ONYX_INTRINSIC_I64_CLZ:      WI(call->token, WI_I64_CLZ); break;
+        case ONYX_INTRINSIC_I64_CTZ:      WI(call->token, WI_I64_CTZ); break;
+        case ONYX_INTRINSIC_I64_POPCNT:   WI(call->token, WI_I64_POPCNT); break;
+        case ONYX_INTRINSIC_I64_AND:      WI(call->token, WI_I64_AND); break;
+        case ONYX_INTRINSIC_I64_OR:       WI(call->token, WI_I64_OR); break;
+        case ONYX_INTRINSIC_I64_XOR:      WI(call->token, WI_I64_XOR); break;
+        case ONYX_INTRINSIC_I64_SHL:      WI(call->token, WI_I64_SHL); break;
+        case ONYX_INTRINSIC_I64_SLR:      WI(call->token, WI_I64_SHR_U); break;
+        case ONYX_INTRINSIC_I64_SAR:      WI(call->token, WI_I64_SHR_S); break;
+        case ONYX_INTRINSIC_I64_ROTL:     WI(call->token, WI_I64_ROTL); break;
+        case ONYX_INTRINSIC_I64_ROTR:     WI(call->token, WI_I64_ROTR); break;
+
+        case ONYX_INTRINSIC_F32_ABS:      WI(call->token, WI_F32_ABS); break;
+        case ONYX_INTRINSIC_F32_CEIL:     WI(call->token, WI_F32_CEIL); break;
+        case ONYX_INTRINSIC_F32_FLOOR:    WI(call->token, WI_F32_FLOOR); break;
+        case ONYX_INTRINSIC_F32_TRUNC:    WI(call->token, WI_F32_TRUNC); break;
+        case ONYX_INTRINSIC_F32_NEAREST:  WI(call->token, WI_F32_NEAREST); break;
+        case ONYX_INTRINSIC_F32_SQRT:     WI(call->token, WI_F32_SQRT); break;
+        case ONYX_INTRINSIC_F32_MIN:      WI(call->token, WI_F32_MIN); break;
+        case ONYX_INTRINSIC_F32_MAX:      WI(call->token, WI_F32_MAX); break;
+        case ONYX_INTRINSIC_F32_COPYSIGN: WI(call->token, WI_F32_COPYSIGN); break;
+
+        case ONYX_INTRINSIC_F64_ABS:      WI(call->token, WI_F64_ABS); break;
+        case ONYX_INTRINSIC_F64_CEIL:     WI(call->token, WI_F64_CEIL); break;
+        case ONYX_INTRINSIC_F64_FLOOR:    WI(call->token, WI_F64_FLOOR); break;
+        case ONYX_INTRINSIC_F64_TRUNC:    WI(call->token, WI_F64_TRUNC); break;
+        case ONYX_INTRINSIC_F64_NEAREST:  WI(call->token, WI_F64_NEAREST); break;
+        case ONYX_INTRINSIC_F64_SQRT:     WI(call->token, WI_F64_SQRT); break;
+        case ONYX_INTRINSIC_F64_MIN:      WI(call->token, WI_F64_MIN); break;
+        case ONYX_INTRINSIC_F64_MAX:      WI(call->token, WI_F64_MAX); break;
+        case ONYX_INTRINSIC_F64_COPYSIGN: WI(call->token, WI_F64_COPYSIGN); break;
+
+        case ONYX_INTRINSIC_I8X16_CONST:
+        case ONYX_INTRINSIC_V128_CONST:   SIMD_INT_CONST_INTRINSIC(u8, 16);   break;
+        case ONYX_INTRINSIC_I16X8_CONST:  SIMD_INT_CONST_INTRINSIC(u16, 8);   break;
+        case ONYX_INTRINSIC_I32X4_CONST:  SIMD_INT_CONST_INTRINSIC(u32, 4);   break;
+        case ONYX_INTRINSIC_I64X2_CONST:  SIMD_INT_CONST_INTRINSIC(u64, 2);   break;
+        case ONYX_INTRINSIC_F32X4_CONST: {
+            f32* byte_buffer = bh_alloc(mod->extended_instr_alloc, 16);
+            bh_arr(AstArgument *) arg_arr = (bh_arr(AstArgument *)) call->args.values;
+            fori (i, 0, 4) {
+                if (arg_arr[i]->value->kind != Ast_Kind_NumLit) {
+                    onyx_report_error(arg_arr[i]->token->pos, Error_Critical,
+                            "SIMD constants expect compile time constants as parameters. The %d%s parameter was not.",
+                            i, bh_num_suffix(i));
+                    *pcode = code;
+                    return;
+                }
+                byte_buffer[i] = (f32) ((AstNumLit *) arg_arr[i]->value)->value.f;
+            }
+            WIP(call->token, WI_V128_CONST, byte_buffer);
+            break;
+        }
+
+        case ONYX_INTRINSIC_F64X2_CONST: {
+            f64* byte_buffer = bh_alloc(mod->extended_instr_alloc, 16);
+            bh_arr(AstArgument *) arg_arr = (bh_arr(AstArgument *)) call->args.values;
+            fori (i, 0, 2) {
+                if (arg_arr[i]->value->kind != Ast_Kind_NumLit) {
+                    onyx_report_error(arg_arr[i]->token->pos, Error_Critical,
+                            "SIMD constants expect compile time constants as parameters. The %d%s parameter was not.",
+                            i, bh_num_suffix(i));
+                    *pcode = code;
+                    return;
+                }
+                byte_buffer[i] = (f64) ((AstNumLit *) arg_arr[i]->value)->value.d;
+            }
+            WIP(call->token, WI_V128_CONST, byte_buffer);
+            break;
+        }
+
+        case ONYX_INTRINSIC_I8X16_SHUFFLE: {
+            u8* byte_buffer = bh_alloc(mod->extended_instr_alloc, 16);
+            bh_arr(AstArgument *) arg_arr = (bh_arr(AstArgument *)) call->args.values;
+
+            // NOTE: There are two parameters that have to be outputted before
+            // the immediate bytes
+            emit_expression(mod, &code, arg_arr[0]->value);
+            emit_expression(mod, &code, arg_arr[1]->value);
+
+            fori (i, 0, 16) {
+                if (arg_arr[i + 2]->value->kind != Ast_Kind_NumLit) {
+                    onyx_report_error(arg_arr[i + 2]->token->pos, Error_Critical,
+                            "SIMD constants expect compile time constants as parameters. The %d%s parameter was not.",
+                            i, bh_num_suffix(i));
+                    *pcode = code;
+                    return;
+                }
+                byte_buffer[i] = (u8) ((AstNumLit *) arg_arr[i + 2]->value)->value.i;
+            }
+            WIP(call->token, WI_I8X16_SHUFFLE, byte_buffer);
+            break;
+        }
+
+        // CLEANUP ALL OF THIS
+        case ONYX_INTRINSIC_I8X16_EXTRACT_LANE_S: SIMD_EXTRACT_LANE_INSTR(WI_I8X16_EXTRACT_LANE_S, ((bh_arr(AstArgument *)) call->args.values)); break;
+        case ONYX_INTRINSIC_I8X16_EXTRACT_LANE_U: SIMD_EXTRACT_LANE_INSTR(WI_I8X16_EXTRACT_LANE_U, ((bh_arr(AstArgument *)) call->args.values)); break;
+        case ONYX_INTRINSIC_I8X16_REPLACE_LANE:   SIMD_REPLACE_LANE_INSTR(WI_I8X16_REPLACE_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
+        case ONYX_INTRINSIC_I16X8_EXTRACT_LANE_S: SIMD_EXTRACT_LANE_INSTR(WI_I16X8_EXTRACT_LANE_S, ((bh_arr(AstArgument *)) call->args.values)); break;
+        case ONYX_INTRINSIC_I16X8_EXTRACT_LANE_U: SIMD_EXTRACT_LANE_INSTR(WI_I16X8_EXTRACT_LANE_U, ((bh_arr(AstArgument *)) call->args.values)); break;
+        case ONYX_INTRINSIC_I16X8_REPLACE_LANE:   SIMD_REPLACE_LANE_INSTR(WI_I16X8_REPLACE_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
+        case ONYX_INTRINSIC_I32X4_EXTRACT_LANE:   SIMD_EXTRACT_LANE_INSTR(WI_I32X4_EXTRACT_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
+        case ONYX_INTRINSIC_I32X4_REPLACE_LANE:   SIMD_REPLACE_LANE_INSTR(WI_I32X4_REPLACE_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
+        case ONYX_INTRINSIC_I64X2_EXTRACT_LANE:   SIMD_EXTRACT_LANE_INSTR(WI_I64X2_EXTRACT_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
+        case ONYX_INTRINSIC_I64X2_REPLACE_LANE:   SIMD_REPLACE_LANE_INSTR(WI_I64X2_REPLACE_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
+        case ONYX_INTRINSIC_F32X4_EXTRACT_LANE:   SIMD_EXTRACT_LANE_INSTR(WI_F32X4_EXTRACT_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
+        case ONYX_INTRINSIC_F32X4_REPLACE_LANE:   SIMD_REPLACE_LANE_INSTR(WI_F32X4_REPLACE_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
+        case ONYX_INTRINSIC_F64X2_EXTRACT_LANE:   SIMD_EXTRACT_LANE_INSTR(WI_F64X2_EXTRACT_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
+        case ONYX_INTRINSIC_F64X2_REPLACE_LANE:   SIMD_REPLACE_LANE_INSTR(WI_F64X2_REPLACE_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
+
+        case ONYX_INTRINSIC_I8X16_SWIZZLE: WI(call->token, WI_I8X16_SWIZZLE); break;
+        case ONYX_INTRINSIC_I8X16_SPLAT:   WI(call->token, WI_I8X16_SPLAT); break;
+        case ONYX_INTRINSIC_I16X8_SPLAT:   WI(call->token, WI_I16X8_SPLAT); break;
+        case ONYX_INTRINSIC_I32X4_SPLAT:   WI(call->token, WI_I32X4_SPLAT); break;
+        case ONYX_INTRINSIC_I64X2_SPLAT:   WI(call->token, WI_I64X2_SPLAT); break;
+        case ONYX_INTRINSIC_F32X4_SPLAT:   WI(call->token, WI_F32X4_SPLAT); break;
+        case ONYX_INTRINSIC_F64X2_SPLAT:   WI(call->token, WI_F64X2_SPLAT); break;
+
+        case ONYX_INTRINSIC_I8X16_EQ:   WI(call->token, WI_I8X16_EQ); break;
+        case ONYX_INTRINSIC_I8X16_NEQ:  WI(call->token, WI_I8X16_NEQ); break;
+        case ONYX_INTRINSIC_I8X16_LT_S: WI(call->token, WI_I8X16_LT_S); break;
+        case ONYX_INTRINSIC_I8X16_LT_U: WI(call->token, WI_I8X16_LT_U); break;
+        case ONYX_INTRINSIC_I8X16_GT_S: WI(call->token, WI_I8X16_GT_S); break;
+        case ONYX_INTRINSIC_I8X16_GT_U: WI(call->token, WI_I8X16_GT_U); break;
+        case ONYX_INTRINSIC_I8X16_LE_S: WI(call->token, WI_I8X16_LE_S); break;
+        case ONYX_INTRINSIC_I8X16_LE_U: WI(call->token, WI_I8X16_LE_U); break;
+        case ONYX_INTRINSIC_I8X16_GE_S: WI(call->token, WI_I8X16_GE_S); break;
+        case ONYX_INTRINSIC_I8X16_GE_U: WI(call->token, WI_I8X16_GE_U); break;
+
+        case ONYX_INTRINSIC_I16X8_EQ:   WI(call->token, WI_I16X8_EQ); break;
+        case ONYX_INTRINSIC_I16X8_NEQ:  WI(call->token, WI_I16X8_NEQ); break;
+        case ONYX_INTRINSIC_I16X8_LT_S: WI(call->token, WI_I16X8_LT_S); break;
+        case ONYX_INTRINSIC_I16X8_LT_U: WI(call->token, WI_I16X8_LT_U); break;
+        case ONYX_INTRINSIC_I16X8_GT_S: WI(call->token, WI_I16X8_GT_S); break;
+        case ONYX_INTRINSIC_I16X8_GT_U: WI(call->token, WI_I16X8_GT_U); break;
+        case ONYX_INTRINSIC_I16X8_LE_S: WI(call->token, WI_I16X8_LE_S); break;
+        case ONYX_INTRINSIC_I16X8_LE_U: WI(call->token, WI_I16X8_LE_U); break;
+        case ONYX_INTRINSIC_I16X8_GE_S: WI(call->token, WI_I16X8_GE_S); break;
+        case ONYX_INTRINSIC_I16X8_GE_U: WI(call->token, WI_I16X8_GE_U); break;
+
+        case ONYX_INTRINSIC_I32X4_EQ:   WI(call->token, WI_I32X4_EQ); break;
+        case ONYX_INTRINSIC_I32X4_NEQ:  WI(call->token, WI_I32X4_NEQ); break;
+        case ONYX_INTRINSIC_I32X4_LT_S: WI(call->token, WI_I32X4_LT_S); break;
+        case ONYX_INTRINSIC_I32X4_LT_U: WI(call->token, WI_I32X4_LT_U); break;
+        case ONYX_INTRINSIC_I32X4_GT_S: WI(call->token, WI_I32X4_GT_S); break;
+        case ONYX_INTRINSIC_I32X4_GT_U: WI(call->token, WI_I32X4_GT_U); break;
+        case ONYX_INTRINSIC_I32X4_LE_S: WI(call->token, WI_I32X4_LE_S); break;
+        case ONYX_INTRINSIC_I32X4_LE_U: WI(call->token, WI_I32X4_LE_U); break;
+        case ONYX_INTRINSIC_I32X4_GE_S: WI(call->token, WI_I32X4_GE_S); break;
+        case ONYX_INTRINSIC_I32X4_GE_U: WI(call->token, WI_I32X4_GE_U); break;
+
+        case ONYX_INTRINSIC_F32X4_EQ:  WI(call->token, WI_F32X4_EQ); break;
+        case ONYX_INTRINSIC_F32X4_NEQ: WI(call->token, WI_F32X4_NEQ); break;
+        case ONYX_INTRINSIC_F32X4_LT:  WI(call->token, WI_F32X4_LT); break;
+        case ONYX_INTRINSIC_F32X4_GT:  WI(call->token, WI_F32X4_GT); break;
+        case ONYX_INTRINSIC_F32X4_LE:  WI(call->token, WI_F32X4_LE); break;
+        case ONYX_INTRINSIC_F32X4_GE:  WI(call->token, WI_F32X4_GE); break;
+
+        case ONYX_INTRINSIC_F64X2_EQ:  WI(call->token, WI_F64X2_EQ); break;
+        case ONYX_INTRINSIC_F64X2_NEQ: WI(call->token, WI_F64X2_NEQ); break;
+        case ONYX_INTRINSIC_F64X2_LT:  WI(call->token, WI_F64X2_LT); break;
+        case ONYX_INTRINSIC_F64X2_GT:  WI(call->token, WI_F64X2_GT); break;
+        case ONYX_INTRINSIC_F64X2_LE:  WI(call->token, WI_F64X2_LE); break;
+        case ONYX_INTRINSIC_F64X2_GE:  WI(call->token, WI_F64X2_GE); break;
+
+        case ONYX_INTRINSIC_V128_NOT:       WI(call->token, WI_V128_NOT); break;
+        case ONYX_INTRINSIC_V128_AND:       WI(call->token, WI_V128_AND); break;
+        case ONYX_INTRINSIC_V128_ANDNOT:    WI(call->token, WI_V128_ANDNOT); break;
+        case ONYX_INTRINSIC_V128_OR:        WI(call->token, WI_V128_OR); break;
+        case ONYX_INTRINSIC_V128_XOR:       WI(call->token, WI_V128_XOR); break;
+        case ONYX_INTRINSIC_V128_BITSELECT: WI(call->token, WI_V128_BITSELECT); break;
+
+        case ONYX_INTRINSIC_I8X16_ABS:            WI(call->token, WI_I8X16_ABS); break;
+        case ONYX_INTRINSIC_I8X16_NEG:            WI(call->token, WI_I8X16_NEG); break;
+        case ONYX_INTRINSIC_I8X16_ANY_TRUE:       WI(call->token, WI_I8X16_ANY_TRUE); break;
+        case ONYX_INTRINSIC_I8X16_ALL_TRUE:       WI(call->token, WI_I8X16_ALL_TRUE); break;
+        case ONYX_INTRINSIC_I8X16_BITMASK:        WI(call->token, WI_I8X16_BITMASK); break;
+        case ONYX_INTRINSIC_I8X16_NARROW_I16X8_S: WI(call->token, WI_I8X16_NARROW_I16X8_S); break;
+        case ONYX_INTRINSIC_I8X16_NARROW_I16X8_U: WI(call->token, WI_I8X16_NARROW_I16X8_U); break;
+        case ONYX_INTRINSIC_I8X16_SHL:            WI(call->token, WI_I8X16_SHL); break;
+        case ONYX_INTRINSIC_I8X16_SHR_S:          WI(call->token, WI_I8X16_SHR_S); break;
+        case ONYX_INTRINSIC_I8X16_SHR_U:          WI(call->token, WI_I8X16_SHR_U); break;
+        case ONYX_INTRINSIC_I8X16_ADD:            WI(call->token, WI_I8X16_ADD); break;
+        case ONYX_INTRINSIC_I8X16_ADD_SAT_S:      WI(call->token, WI_I8X16_ADD_SAT_S); break;
+        case ONYX_INTRINSIC_I8X16_ADD_SAT_U:      WI(call->token, WI_I8X16_ADD_SAT_U); break;
+        case ONYX_INTRINSIC_I8X16_SUB:            WI(call->token, WI_I8X16_SUB); break;
+        case ONYX_INTRINSIC_I8X16_SUB_SAT_S:      WI(call->token, WI_I8X16_SUB_SAT_S); break;
+        case ONYX_INTRINSIC_I8X16_SUB_SAT_U:      WI(call->token, WI_I8X16_SUB_SAT_U); break;
+        case ONYX_INTRINSIC_I8X16_MIN_S:          WI(call->token, WI_I8X16_MIN_S); break;
+        case ONYX_INTRINSIC_I8X16_MIN_U:          WI(call->token, WI_I8X16_MIN_U); break;
+        case ONYX_INTRINSIC_I8X16_MAX_S:          WI(call->token, WI_I8X16_MAX_S); break;
+        case ONYX_INTRINSIC_I8X16_MAX_U:          WI(call->token, WI_I8X16_MAX_U); break;
+        case ONYX_INTRINSIC_I8X16_AVGR_U:         WI(call->token, WI_I8X16_AVGR_U); break;
+
+        case ONYX_INTRINSIC_I16X8_ABS:                WI(call->token, WI_I16X8_ABS); break;
+        case ONYX_INTRINSIC_I16X8_NEG:                WI(call->token, WI_I16X8_NEG); break;
+        case ONYX_INTRINSIC_I16X8_ANY_TRUE:           WI(call->token, WI_I16X8_ANY_TRUE); break;
+        case ONYX_INTRINSIC_I16X8_ALL_TRUE:           WI(call->token, WI_I16X8_ALL_TRUE); break;
+        case ONYX_INTRINSIC_I16X8_BITMASK:            WI(call->token, WI_I16X8_BITMASK); break;
+        case ONYX_INTRINSIC_I16X8_NARROW_I32X4_S:     WI(call->token, WI_I16X8_NARROW_I32X4_S); break;
+        case ONYX_INTRINSIC_I16X8_NARROW_I32X4_U:     WI(call->token, WI_I16X8_NARROW_I32X4_U); break;
+        case ONYX_INTRINSIC_I16X8_WIDEN_LOW_I8X16_S:  WI(call->token, WI_I16X8_WIDEN_LOW_I8X16_S); break;
+        case ONYX_INTRINSIC_I16X8_WIDEN_HIGH_I8X16_S: WI(call->token, WI_I16X8_WIDEN_HIGH_I8X16_S); break;
+        case ONYX_INTRINSIC_I16X8_WIDEN_LOW_I8X16_U:  WI(call->token, WI_I16X8_WIDEN_LOW_I8X16_U); break;
+        case ONYX_INTRINSIC_I16X8_WIDEN_HIGH_I8X16_U: WI(call->token, WI_I16X8_WIDEN_HIGH_I8X16_U); break;
+        case ONYX_INTRINSIC_I16X8_SHL:                WI(call->token, WI_I16X8_SHL); break;
+        case ONYX_INTRINSIC_I16X8_SHR_S:              WI(call->token, WI_I16X8_SHR_S); break;
+        case ONYX_INTRINSIC_I16X8_SHR_U:              WI(call->token, WI_I16X8_SHR_U); break;
+        case ONYX_INTRINSIC_I16X8_ADD:                WI(call->token, WI_I16X8_ADD); break;
+        case ONYX_INTRINSIC_I16X8_ADD_SAT_S:          WI(call->token, WI_I16X8_ADD_SAT_S); break;
+        case ONYX_INTRINSIC_I16X8_ADD_SAT_U:          WI(call->token, WI_I16X8_ADD_SAT_U); break;
+        case ONYX_INTRINSIC_I16X8_SUB:                WI(call->token, WI_I16X8_SUB); break;
+        case ONYX_INTRINSIC_I16X8_SUB_SAT_S:          WI(call->token, WI_I16X8_SUB_SAT_S); break;
+        case ONYX_INTRINSIC_I16X8_SUB_SAT_U:          WI(call->token, WI_I16X8_SUB_SAT_U); break;
+        case ONYX_INTRINSIC_I16X8_MUL:                WI(call->token, WI_I16X8_MUL); break;
+        case ONYX_INTRINSIC_I16X8_MIN_S:              WI(call->token, WI_I16X8_MIN_S); break;
+        case ONYX_INTRINSIC_I16X8_MIN_U:              WI(call->token, WI_I16X8_MIN_U); break;
+        case ONYX_INTRINSIC_I16X8_MAX_S:              WI(call->token, WI_I16X8_MAX_S); break;
+        case ONYX_INTRINSIC_I16X8_MAX_U:              WI(call->token, WI_I16X8_MAX_U); break;
+        case ONYX_INTRINSIC_I16X8_AVGR_U:             WI(call->token, WI_I16X8_AVGR_U); break;
+
+        case ONYX_INTRINSIC_I32X4_ABS:                WI(call->token, WI_I32X4_ABS); break;
+        case ONYX_INTRINSIC_I32X4_NEG:                WI(call->token, WI_I32X4_NEG); break;
+        case ONYX_INTRINSIC_I32X4_ANY_TRUE:           WI(call->token, WI_I32X4_ANY_TRUE); break;
+        case ONYX_INTRINSIC_I32X4_ALL_TRUE:           WI(call->token, WI_I32X4_ALL_TRUE); break;
+        case ONYX_INTRINSIC_I32X4_BITMASK:            WI(call->token, WI_I32X4_BITMASK); break;
+        case ONYX_INTRINSIC_I32X4_WIDEN_LOW_I16X8_S:  WI(call->token, WI_I32X4_WIDEN_LOW_I16X8_S); break;
+        case ONYX_INTRINSIC_I32X4_WIDEN_HIGH_I16X8_S: WI(call->token, WI_I32X4_WIDEN_HIGH_I16X8_S); break;
+        case ONYX_INTRINSIC_I32X4_WIDEN_LOW_I16X8_U:  WI(call->token, WI_I32X4_WIDEN_LOW_I16X8_U); break;
+        case ONYX_INTRINSIC_I32X4_WIDEN_HIGH_I16X8_U: WI(call->token, WI_I32X4_WIDEN_HIGH_I16X8_U); break;
+        case ONYX_INTRINSIC_I32X4_SHL:                WI(call->token, WI_I32X4_SHL); break;
+        case ONYX_INTRINSIC_I32X4_SHR_S:              WI(call->token, WI_I32X4_SHR_S); break;
+        case ONYX_INTRINSIC_I32X4_SHR_U:              WI(call->token, WI_I32X4_SHR_U); break;
+        case ONYX_INTRINSIC_I32X4_ADD:                WI(call->token, WI_I32X4_ADD); break;
+        case ONYX_INTRINSIC_I32X4_SUB:                WI(call->token, WI_I32X4_SUB); break;
+        case ONYX_INTRINSIC_I32X4_MUL:                WI(call->token, WI_I32X4_MUL); break;
+        case ONYX_INTRINSIC_I32X4_MIN_S:              WI(call->token, WI_I32X4_MIN_S); break;
+        case ONYX_INTRINSIC_I32X4_MIN_U:              WI(call->token, WI_I32X4_MIN_U); break;
+        case ONYX_INTRINSIC_I32X4_MAX_S:              WI(call->token, WI_I32X4_MAX_S); break;
+        case ONYX_INTRINSIC_I32X4_MAX_U:              WI(call->token, WI_I32X4_MAX_U); break;
+
+        case ONYX_INTRINSIC_I64X2_NEG:   WI(call->token, WI_I64X2_NEG); break;
+        case ONYX_INTRINSIC_I64X2_SHL:   WI(call->token, WI_I64X2_SHL); break;
+        case ONYX_INTRINSIC_I64X2_SHR_S: WI(call->token, WI_I64X2_SHR_S); break;
+        case ONYX_INTRINSIC_I64X2_SHR_U: WI(call->token, WI_I64X2_SHR_U); break;
+        case ONYX_INTRINSIC_I64X2_ADD:   WI(call->token, WI_I64X2_ADD); break;
+        case ONYX_INTRINSIC_I64X2_SUB:   WI(call->token, WI_I64X2_SUB); break;
+        case ONYX_INTRINSIC_I64X2_MUL:   WI(call->token, WI_I64X2_MUL); break;
+
+        case ONYX_INTRINSIC_F32X4_ABS:  WI(call->token, WI_F32X4_ABS); break;
+        case ONYX_INTRINSIC_F32X4_NEG:  WI(call->token, WI_F32X4_NEG); break;
+        case ONYX_INTRINSIC_F32X4_SQRT: WI(call->token, WI_F32X4_SQRT); break;
+        case ONYX_INTRINSIC_F32X4_ADD:  WI(call->token, WI_F32X4_ADD); break;
+        case ONYX_INTRINSIC_F32X4_SUB:  WI(call->token, WI_F32X4_SUB); break;
+        case ONYX_INTRINSIC_F32X4_MUL:  WI(call->token, WI_F32X4_MUL); break;
+        case ONYX_INTRINSIC_F32X4_DIV:  WI(call->token, WI_F32X4_DIV); break;
+        case ONYX_INTRINSIC_F32X4_MIN:  WI(call->token, WI_F32X4_MIN); break;
+        case ONYX_INTRINSIC_F32X4_MAX:  WI(call->token, WI_F32X4_MAX); break;
+
+        case ONYX_INTRINSIC_F64X2_ABS:  WI(call->token, WI_F64X2_ABS); break;
+        case ONYX_INTRINSIC_F64X2_NEG:  WI(call->token, WI_F64X2_NEG); break;
+        case ONYX_INTRINSIC_F64X2_SQRT: WI(call->token, WI_F64X2_SQRT); break;
+        case ONYX_INTRINSIC_F64X2_ADD:  WI(call->token, WI_F64X2_ADD); break;
+        case ONYX_INTRINSIC_F64X2_SUB:  WI(call->token, WI_F64X2_SUB); break;
+        case ONYX_INTRINSIC_F64X2_MUL:  WI(call->token, WI_F64X2_MUL); break;
+        case ONYX_INTRINSIC_F64X2_DIV:  WI(call->token, WI_F64X2_DIV); break;
+        case ONYX_INTRINSIC_F64X2_MIN:  WI(call->token, WI_F64X2_MIN); break;
+        case ONYX_INTRINSIC_F64X2_MAX:  WI(call->token, WI_F64X2_MAX); break;
+
+        case ONYX_INTRINSIC_I32X4_TRUNC_SAT_F32X4_S: WI(call->token, WI_I32X4_TRUNC_SAT_F32X4_S); break;
+        case ONYX_INTRINSIC_I32X4_TRUNC_SAT_F32X4_U: WI(call->token, WI_I32X4_TRUNC_SAT_F32X4_U); break;
+        case ONYX_INTRINSIC_F32X4_CONVERT_I32X4_S:   WI(call->token, WI_F32X4_CONVERT_I32X4_S); break;
+        case ONYX_INTRINSIC_F32X4_CONVERT_I32X4_U:   WI(call->token, WI_F32X4_CONVERT_I32X4_U); break;
+
+        case ONYX_INTRINSIC_ATOMIC_WAIT: {
+            Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
+            emit_intrinsic_atomic_wait(mod, &code, atomic_type, call->token);
+            break;
+        }
+
+        case ONYX_INTRINSIC_ATOMIC_NOTIFY: {
+            emit_intrinsic_atomic_notify(mod, &code);
+            break;
+        }
+
+        case ONYX_INTRINSIC_ATOMIC_FENCE: {
+            emit_intrinsic_atomic_fence(mod, &code);
+            break;
+        }
+
+        case ONYX_INTRINSIC_ATOMIC_LOAD: {
+            Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
+            emit_intrinsic_atomic_load(mod, &code, atomic_type, call->token);
+            break;
+        }
+
+        case ONYX_INTRINSIC_ATOMIC_STORE: {
+            Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
+            emit_intrinsic_atomic_store(mod, &code, atomic_type, call->token);
+            break;
+        }
+
+        case ONYX_INTRINSIC_ATOMIC_ADD: {
+            Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
+            emit_intrinsic_atomic_add(mod, &code, atomic_type, call->token);
+            break;
+        }
+
+        case ONYX_INTRINSIC_ATOMIC_SUB: {
+            Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
+            emit_intrinsic_atomic_sub(mod, &code, atomic_type, call->token);
+            break;
+        }
+
+        case ONYX_INTRINSIC_ATOMIC_AND: {
+            Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
+            emit_intrinsic_atomic_and(mod, &code, atomic_type, call->token);
+            break;
+        }
+
+        case ONYX_INTRINSIC_ATOMIC_OR: {
+            Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
+            emit_intrinsic_atomic_or(mod, &code, atomic_type, call->token);
+            break;
+        }
+
+        case ONYX_INTRINSIC_ATOMIC_XOR: {
+            Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
+            emit_intrinsic_atomic_xor(mod, &code, atomic_type, call->token);
+            break;
+        }
+
+        case ONYX_INTRINSIC_ATOMIC_XCHG: {
+            Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
+            emit_intrinsic_atomic_xchg(mod, &code, atomic_type, call->token);
+            break;
+        }
+
+        case ONYX_INTRINSIC_ATOMIC_CMPXCHG: {
+            Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
+            emit_intrinsic_atomic_cmpxchg(mod, &code, atomic_type, call->token);
+            break;
+        }
+
+        default: assert(("Unsupported intrinsic", 0));
+    }
+
+    *pcode = code;
+}
+
+EMIT_FUNC(subscript_location, AstSubscript* sub, u64* offset_return) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    emit_expression(mod, &code, sub->expr);
+    if (sub->elem_size != 1) {
+        WID(sub->token, WI_PTR_CONST, sub->elem_size);
+        WI(sub->token, WI_PTR_MUL);
+    }
+
+    // CLEANUP: This is one dense clusterf**k of code...
+    u64 offset = 0;
+    if (sub->addr->kind == Ast_Kind_Subscript
+        && sub->addr->type->kind == Type_Kind_Array) {
+        emit_subscript_location(mod, &code, (AstSubscript *) sub->addr, &offset);
+    } else if (sub->addr->kind == Ast_Kind_Field_Access
+        && sub->addr->type->kind == Type_Kind_Array) {
+        emit_field_access_location(mod, &code, (AstFieldAccess *) sub->addr, &offset);
+    } else if ((sub->addr->kind == Ast_Kind_Local || sub->addr->kind == Ast_Kind_Param)
+        && sub->addr->type->kind == Type_Kind_Array) {
+        emit_local_location(mod, &code, (AstLocal *) sub->addr, &offset);
+    } else if (sub->addr->kind == Ast_Kind_Memres
+        && sub->addr->type->kind != Type_Kind_Array) {
+        emit_memory_reservation_location(mod, &code, (AstMemRes *) sub->addr);
+    } else {
+        emit_expression(mod, &code, sub->addr);
+    }
+
+    WI(sub->token, WI_PTR_ADD);
+
+    *offset_return += offset;
+
+    *pcode = code;
+}
+
+EMIT_FUNC(field_access_location, AstFieldAccess* field, u64* offset_return) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    u64 offset = field->offset;
+    AstTyped* source_expr = field->expr;
+    while (source_expr->kind == Ast_Kind_Field_Access
+            && type_is_structlike_strict(source_expr->type)) {
+        offset += ((AstFieldAccess *) source_expr)->offset;
+        source_expr = (AstTyped *) ((AstFieldAccess *) source_expr)->expr;
+    }
+
+    if (source_expr->kind == Ast_Kind_Subscript
+        && source_expr->type->kind != Type_Kind_Pointer) {
+        u64 o2 = 0;
+        emit_subscript_location(mod, &code, (AstSubscript *) source_expr, &o2);
+        offset += o2;
+
+    } else if ((source_expr->kind == Ast_Kind_Local || source_expr->kind == Ast_Kind_Param)
+        && source_expr->type->kind != Type_Kind_Pointer) {
+        u64 o2 = 0;
+        emit_local_location(mod, &code, (AstLocal *) source_expr, &o2);
+        offset += o2;
+
+    } else if (source_expr->kind == Ast_Kind_Memres
+        && source_expr->type->kind != Type_Kind_Pointer) {
+        emit_memory_reservation_location(mod, &code, (AstMemRes *) source_expr);
+
+    } else {
+        emit_expression(mod, &code, source_expr);
+    }
+
+    *offset_return = offset;
+
+    *pcode = code;
+}
+
+EMIT_FUNC(memory_reservation_location, AstMemRes* memres) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    if (memres->threadlocal) {
+        u64 tls_base_idx = bh_imap_get(&mod->index_map, (u64) &builtin_tls_base);
+        WID(NULL, WI_PTR_CONST, memres->tls_offset);
+        WIL(NULL, WI_GLOBAL_GET, tls_base_idx);
+        WI(NULL, WI_PTR_ADD);
+
+    } else {
+        // :ProperLinking
+        assert(memres->data_id != 0);
+        emit_data_relocation(mod, &code, memres->data_id);
+    }
+
+    *pcode = code;
+}
+
+EMIT_FUNC(local_location, AstLocal* local, u64* offset_return) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    u64 local_offset = (u64) bh_imap_get(&mod->local_map, (u64) local);
+
+    if (local_offset & LOCAL_IS_WASM) {
+        // This is a weird condition but it is relied on in a couple places including
+        // passing non-simple structs by value.             -brendanfh 2020/09/18
+        WIL(NULL, WI_LOCAL_GET, local_offset);
+
+    } else {
+        WIL(NULL, WI_LOCAL_GET, mod->stack_base_idx);
+
+        *offset_return += local_offset;
+    }
+
+    *pcode = code;
+}
+
+EMIT_FUNC(struct_lval, AstTyped* lval) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    assert(type_is_structlike_strict(lval->type));
+
+    u64 offset = 0;
+    emit_location_return_offset(mod, &code, lval, &offset);
+    emit_compound_store(mod, &code, lval->type, offset, 1);
+
+    *pcode = code;
+}
+
+EMIT_FUNC(compound_load, Type* type, u64 offset) {
+    bh_arr(WasmInstruction) code = *pcode;
+    i32 mem_count = type_linear_member_count(type);
+    TypeWithOffset two;
+
+    if (mem_count == 1) {
+        type_linear_member_lookup(type, 0, &two);
+        emit_load_instruction(mod, &code, two.type, offset + two.offset); // two.offset should be 0
+    } else {
+        u64 tmp_idx = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+        WIL(NULL, WI_LOCAL_TEE, tmp_idx);
+
+        fori (i, 0, mem_count) {
+            type_linear_member_lookup(type, i, &two);
+            if (i != 0) WIL(NULL, WI_LOCAL_GET, tmp_idx);
+            emit_load_instruction(mod, &code, two.type, offset + two.offset);
+        }
+
+        local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+    }
+
+    *pcode = code;
+    return;
+}
+
+EMIT_FUNC(compound_store, Type* type, u64 offset, b32 location_first) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    TypeWithOffset two;
+
+    u64 loc_idx = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+    if (location_first) WIL(NULL, WI_LOCAL_SET, loc_idx);
+
+    i32 elem_count = type_linear_member_count(type);
+    u64 *temp_locals = bh_alloc_array(global_scratch_allocator, u64, elem_count);
+
+    forir (i, elem_count - 1, 0) {
+        type_linear_member_lookup(type, i, &two);
+
+        WasmType wt = onyx_type_to_wasm_type(two.type);
+        temp_locals[i] = local_raw_allocate(mod->local_alloc, wt);
+        WIL(NULL, WI_LOCAL_SET, temp_locals[i]);
+    }
+
+    if (!location_first) WIL(NULL, WI_LOCAL_SET, loc_idx);
+
+    fori (i, 0, elem_count) {
+        type_linear_member_lookup(type, i, &two);
+
+        u64 tmp_idx = temp_locals[i];
+        WIL(NULL, WI_LOCAL_GET, loc_idx);
+        WIL(NULL, WI_LOCAL_GET, tmp_idx);
+        emit_store_instruction(mod, &code, two.type, offset + two.offset);
+
+        WasmType wt = onyx_type_to_wasm_type(two.type);
+        local_raw_free(mod->local_alloc, wt);
+    }
+
+    local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+
+    // This shouldn't be necessary because the scratch allocator doesn't free.
+    bh_free(global_scratch_allocator, temp_locals);
+
+    *pcode = code;
+}
+
+EMIT_FUNC(struct_literal, AstStructLiteral* sl) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    bh_arr_each(AstTyped *, val, sl->args.values) {
+        emit_expression(mod, &code, *val);
+    }
+
+    *pcode = code;
+}
+
+EMIT_FUNC(array_store, Type* type, u32 offset) {
+    assert(type->kind == Type_Kind_Array);
+    bh_arr(WasmInstruction) code = *pcode;
+
+    Type* elem_type = type;
+    u32 elem_count = 1;
+    while (elem_type->kind == Type_Kind_Array) {
+        elem_count *= elem_type->Array.count;
+        elem_type = elem_type->Array.elem;
+    }
+    u32 elem_size = type_size_of(elem_type);
+
+    u64 lptr_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+    u64 rptr_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+    WIL(NULL, WI_LOCAL_SET, rptr_local);
+    WIL(NULL, WI_LOCAL_SET, lptr_local);
+
+    WIL(NULL, WI_LOCAL_GET, rptr_local);
+    WID(NULL, WI_I32_CONST, 0);
+    WI(NULL, WI_I32_NE);
+    emit_enter_structured_block(mod, &code, SBT_Basic_If, NULL);
+
+    //
+    // CLEANUP: Most of these cases could be much shorter if they used existing intrinsics.
+    //
+    if (elem_count <= 2) {
+        // Inline copying for a small number of elements. It still may be faster to do this in a tight loop.
+
+        fori (i, 0, elem_count) {
+            if (bh_arr_last(code).type == WI_LOCAL_SET && (u64) bh_arr_last(code).data.l == lptr_local)
+                bh_arr_last(code).type = WI_LOCAL_TEE;
+            else
+                WIL(NULL, WI_LOCAL_GET, lptr_local);
+
+            WIL(NULL, WI_LOCAL_GET, rptr_local);
+            emit_load_instruction(mod, &code, elem_type, i * elem_size);
+
+            emit_store_instruction(mod, &code, elem_type, i * elem_size + offset);
+        }
+
+    } else if (context.options->use_post_mvp_features) {
+        // Use a simple memory copy if it is available. This may be what happens in the case below too at a later time.
+
+        if (bh_arr_last(code).type == WI_LOCAL_SET && (u64) bh_arr_last(code).data.l == lptr_local)
+            bh_arr_last(code).type = WI_LOCAL_TEE;
+        else
+            WIL(NULL, WI_LOCAL_GET, lptr_local);
+
+        if (offset != 0) {
+            WIL(NULL, WI_PTR_CONST, offset);
+            WI(NULL, WI_PTR_ADD);
+        }
+
+        WIL(NULL, WI_LOCAL_GET, rptr_local);
+        WIL(NULL, WI_I32_CONST, elem_count * elem_size);
+        WI(NULL, WI_MEMORY_COPY);
+
+    } else {
+        // Emit a loop that copies the memory. This could be switched to a tight loop that just copies word per word.
+
+        u64 offset_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+        WIL(NULL, WI_PTR_CONST, 0);
+        WIL(NULL, WI_LOCAL_SET, offset_local);
+
+        WID(NULL, WI_BLOCK_START, 0x40);
+        WID(NULL, WI_LOOP_START, 0x40);
+            WIL(NULL, WI_LOCAL_GET, offset_local);
+            WIL(NULL, WI_LOCAL_GET, lptr_local);
+            WI(NULL, WI_PTR_ADD);
+
+            WIL(NULL, WI_LOCAL_GET, offset_local);
+            WIL(NULL, WI_LOCAL_GET, rptr_local);
+            WI(NULL, WI_PTR_ADD);
+
+            emit_load_instruction(mod, &code, elem_type, 0);
+            emit_store_instruction(mod, &code, elem_type, offset);
+
+            WIL(NULL, WI_LOCAL_GET, offset_local);
+            WIL(NULL, WI_PTR_CONST, elem_size);
+            WI(NULL, WI_PTR_ADD);
+            WIL(NULL, WI_LOCAL_TEE, offset_local);
+
+            WIL(NULL, WI_PTR_CONST, elem_count * elem_size);
+            WI(NULL, WI_PTR_GE);
+            WID(NULL, WI_COND_JUMP, 0x01);
+
+            WID(NULL, WI_JUMP, 0x00);
+
+        WI(NULL, WI_LOOP_END);
+        WI(NULL, WI_BLOCK_END);
+
+        local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+    }
+
+    WI(NULL, WI_ELSE);
+
+    { // If the source ptr is null (0), then just copy in 0 bytes.
+        WIL(NULL, WI_LOCAL_GET, lptr_local);
+        if (offset != 0) {
+            WIL(NULL, WI_PTR_CONST, offset);
+            WI(NULL, WI_PTR_ADD);
+        }
+
+        WIL(NULL, WI_I32_CONST, 0);
+
+        WIL(NULL, WI_I32_CONST, elem_count * elem_size);
+
+        if (context.options->use_post_mvp_features) {
+            WI(NULL, WI_MEMORY_FILL);
+        } else {
+            emit_intrinsic_memory_fill(mod, &code);
+        }
+    }
+
+    local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+    local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+
+    emit_leave_structured_block(mod, &code);
+    *pcode = code;
+    return;
+}
+
+EMIT_FUNC(array_literal, AstArrayLiteral* al) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    u64 local_offset = (u64) bh_imap_get(&mod->local_map, (u64) al);
+    assert((local_offset & LOCAL_IS_WASM) == 0);
+
+    assert(al->type->kind == Type_Kind_Array);
+
+    u32 elem_size = type_size_of(al->type->Array.elem);
+
+    fori (i, 0, al->type->Array.count) {
+        WIL(al->token, WI_LOCAL_GET, mod->stack_base_idx);
+        emit_expression(mod, &code, al->values[i]);
+        emit_store_instruction(mod, &code, al->type->Array.elem, local_offset + i * elem_size);
+    }
+
+    WIL(al->token, WI_LOCAL_GET, mod->stack_base_idx);
+    WIL(al->token, WI_PTR_CONST, local_offset);
+    WI(al->token, WI_PTR_ADD);
+
+    *pcode = code;
+}
+
+EMIT_FUNC(range_literal, AstRangeLiteral* range) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    emit_expression(mod, &code, range->low);
+    emit_expression(mod, &code, range->high);
+    emit_expression(mod, &code, range->step);
+
+    *pcode = code;
+}
+
+EMIT_FUNC(if_expression, AstIfExpression* if_expr) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    u64 offset = 0;
+    u64 result_local    = local_allocate(mod->local_alloc, (AstTyped *) if_expr);
+    b32 result_is_local = (b32) ((result_local & LOCAL_IS_WASM) != 0);
+    bh_imap_put(&mod->local_map, (u64) if_expr, result_local);
+
+    emit_expression(mod, &code, if_expr->cond);
+
+    emit_enter_structured_block(mod, &code, SBT_Basic_If, if_expr->token);
+        if (!result_is_local) emit_local_location(mod, &code, (AstLocal *) if_expr, &offset);
+
+        emit_expression(mod, &code, if_expr->true_expr);
+
+        if (!result_is_local) emit_store_instruction(mod, &code, if_expr->type, offset);
+        else                  WIL(if_expr->token, WI_LOCAL_SET, result_local);
+
+    offset = 0;
+    WI(if_expr->token, WI_ELSE);
+        if (!result_is_local) emit_local_location(mod, &code, (AstLocal *) if_expr, &offset);
+
+        emit_expression(mod, &code, if_expr->false_expr);
+
+        if (!result_is_local) emit_store_instruction(mod, &code, if_expr->type, offset);
+        else                  WIL(if_expr->token, WI_LOCAL_SET, result_local);
+
+    emit_leave_structured_block(mod, &code);
+
+    offset = 0;
+    if (!result_is_local) {
+        emit_local_location(mod, &code, (AstLocal *) if_expr, &offset);
+        emit_load_instruction(mod, &code, if_expr->type, offset);
+
+    } else {
+        WIL(if_expr->token, WI_LOCAL_GET, result_local);
+    }
+
+    local_free(mod->local_alloc, (AstTyped *) if_expr);
+
+    *pcode = code;
+}
+
+EMIT_FUNC(do_block, AstDoBlock* doblock) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    u64 result_local    = local_allocate(mod->local_alloc, (AstTyped *) doblock);
+    b32 result_is_local = (b32) ((result_local & LOCAL_IS_WASM) != 0);
+
+    bh_imap_put(&mod->local_map, (u64) doblock, result_local);
+    bh_arr_push(mod->return_location_stack, (AstLocal *) doblock);
+
+    emit_block(mod, &code, doblock->block, 1);
+
+    u64 offset = 0;
+    if (!result_is_local) {
+        emit_local_location(mod, &code, (AstLocal *) doblock, &offset);
+        emit_load_instruction(mod, &code, doblock->type, offset);
+
+    } else {
+        WIL(doblock->block->token, WI_LOCAL_GET, result_local);
+    }
+
+    bh_arr_pop(mod->return_location_stack);
+    local_free(mod->local_alloc, (AstTyped *) doblock);
+
+    *pcode = code;
+}
+
+EMIT_FUNC(location_return_offset, AstTyped* expr, u64* offset_return) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    expr = (AstTyped *) strip_aliases((AstNode *) expr);
+
+    switch (expr->kind) {
+        case Ast_Kind_Param:
+        case Ast_Kind_Local:
+        case Ast_Kind_Array_Literal:
+        case Ast_Kind_Struct_Literal: {
+            emit_local_location(mod, &code, (AstLocal *) expr, offset_return);
+            break;
+        }
+
+        case Ast_Kind_Dereference: {
+            emit_expression(mod, &code, ((AstDereference *) expr)->expr);
+            *offset_return = 0;
+            break;
+        }
+
+        case Ast_Kind_Subscript: {
+            AstSubscript* sub = (AstSubscript *) expr;
+            emit_subscript_location(mod, &code, sub, offset_return);
+            break;
+        }
+
+        case Ast_Kind_Field_Access: {
+            AstFieldAccess* field = (AstFieldAccess *) expr;
+            emit_field_access_location(mod, &code, field, offset_return);
+            break;
+        }
+
+        case Ast_Kind_Memres: {
+            AstMemRes* memres = (AstMemRes *) expr;
+            emit_memory_reservation_location(mod, &code, memres);
+            *offset_return = 0;
+            break;
+        }
+
+        default: {
+            onyx_report_error(expr->token->pos, Error_Critical, "Unable to generate location for '%s'.", onyx_ast_node_kind_string(expr->kind));
+            break;
+        }
+    }
+
+    *pcode = code;
+}
+
+EMIT_FUNC(location, AstTyped* expr) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    u64 offset = 0;
+    emit_location_return_offset(mod, &code, expr, &offset);
+    if (offset != 0) {
+        WID(NULL, WI_PTR_CONST, offset);
+        WI(NULL, WI_PTR_ADD);
+    }
+
+    *pcode = code;
+}
+
+EMIT_FUNC(expression, AstTyped* expr) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    if (node_is_type((AstNode *) expr)) {
+        AstType* type = (AstType *) expr;
+        if (type->flags & Ast_Flag_Expr_Ignored) return;
+
+        if (type->type_id != 0) {
+            WID(NULL, WI_I32_CONST, ((AstType *) expr)->type_id);
+        } else {
+            Type* t = type_build_from_ast(context.ast_alloc, type);
+            WID(NULL, WI_I32_CONST, t->id);
+        }
+
+
+        *pcode = code;
+        return;
+    }
+
+    switch (expr->kind) {
+        case Ast_Kind_Param: {
+            AstLocal* param = (AstLocal *) expr;
+            u64 localidx = bh_imap_get(&mod->local_map, (u64) param);
+
+            switch (type_get_param_pass(param->type)) {
+                case Param_Pass_By_Value: {
+                    if (type_is_structlike_strict(expr->type)) {
+                        u32 mem_count = type_structlike_mem_count(expr->type);
+                        fori (idx, 0, mem_count) WIL(NULL, WI_LOCAL_GET, localidx + idx);
+
+                    } else {
+                        assert(localidx & LOCAL_IS_WASM);
+                        WIL(NULL, WI_LOCAL_GET, localidx);
+                    }
+                    break;
+                }
+
+                case Param_Pass_By_Implicit_Pointer: {
+                    assert(localidx & LOCAL_IS_WASM);
+                    WIL(NULL, WI_LOCAL_GET, localidx);
+                    emit_load_instruction(mod, &code, expr->type, 0);
+                    break;
+                }
+
+                default: assert(0);
+            }
+
+            break;
+        }
+
+        case Ast_Kind_Local: {
+            u64 tmp = bh_imap_get(&mod->local_map, (u64) expr);
+
+            if (tmp & LOCAL_IS_WASM) {
+                if (bh_arr_last(code).type == WI_LOCAL_SET && (u64) bh_arr_last(code).data.l == tmp) {
+                    bh_arr_last(code).type = WI_LOCAL_TEE;
+                } else {
+                    WIL(NULL, WI_LOCAL_GET, tmp);
+                }
+
+            } else {
+                u64 offset = 0;
+                emit_local_location(mod, &code, (AstLocal *) expr, &offset);
+
+                if (expr->type->kind != Type_Kind_Array) {
+                    emit_load_instruction(mod, &code, expr->type, offset);
+                } else if (offset != 0) {
+                    WID(NULL, WI_PTR_CONST, offset);
+                    WI(NULL, WI_PTR_ADD);
+                }
+            }
+
+            break;
+        }
+
+        case Ast_Kind_Global: {
+            i32 globalidx = (i32) bh_imap_get(&mod->index_map, (u64) expr);
+
+            WID(NULL, WI_GLOBAL_GET, globalidx);
+            break;
+        }
+
+        case Ast_Kind_NumLit: {
+            AstNumLit* lit = (AstNumLit *) expr;
+            WasmType lit_type = onyx_type_to_wasm_type(lit->type);
+            WasmInstruction instr = { WI_NOP, 0 };
+
+            if (lit_type == WASM_TYPE_INT32) {
+                instr.type = WI_I32_CONST;
+                instr.data.i1 = lit->value.i;
+            } else if (lit_type == WASM_TYPE_INT64) {
+                instr.type = WI_I64_CONST;
+                instr.data.l = lit->value.l;
+            } else if (lit_type == WASM_TYPE_FLOAT32) {
+                instr.type = WI_F32_CONST;
+                instr.data.f = lit->value.f;
+            } else if (lit_type == WASM_TYPE_FLOAT64) {
+                instr.type = WI_F64_CONST;
+                instr.data.d = lit->value.d;
+            }
+
+            WIR(NULL, instr);
+            break;
+        }
+
+        case Ast_Kind_StrLit: {
+            // :ProperLinking
+            AstStrLit *strlit = (AstStrLit *) expr;
+            assert(strlit->data_id > 0);
+            emit_data_relocation(mod, &code, strlit->data_id);
+
+            if (strlit->is_cstr == 0)
+                WID(NULL, WI_I32_CONST, strlit->length);
+            break;
+        }
+
+        case Ast_Kind_Struct_Literal: {
+            emit_struct_literal(mod, &code, (AstStructLiteral *) expr);
+            break;
+        }
+
+        case Ast_Kind_Array_Literal: {
+            emit_local_allocation(mod, &code, expr);
+            emit_array_literal(mod, &code, (AstArrayLiteral *) expr);
+            break;
+        }
+
+        case Ast_Kind_Range_Literal: {
+            emit_range_literal(mod, &code, (AstRangeLiteral *) expr);
+            break;
+        }
+
+        case Ast_Kind_Function: {
+            i32 elemidx = get_element_idx(mod, (AstFunction *) expr);
+
+            WID(NULL, WI_I32_CONST, elemidx);
+            break;
+        }
+
+        case Ast_Kind_Block:          emit_block(mod, &code, (AstBlock *) expr, 1); break;
+        case Ast_Kind_Do_Block:       emit_do_block(mod, &code, (AstDoBlock *) expr); break;
+        case Ast_Kind_Call:           emit_call(mod, &code, (AstCall *) expr); break;
+        case Ast_Kind_Argument:       emit_expression(mod, &code, ((AstArgument *) expr)->value); break;
+        case Ast_Kind_Intrinsic_Call: emit_intrinsic_call(mod, &code, (AstCall *) expr); break;
+        case Ast_Kind_Binary_Op:      emit_binop(mod, &code, (AstBinaryOp *) expr); break;
+        case Ast_Kind_Unary_Op:       emit_unaryop(mod, &code, (AstUnaryOp *) expr); break;
+        case Ast_Kind_Alias:          emit_expression(mod, &code, ((AstAlias *) expr)->alias); break;
+
+        case Ast_Kind_Address_Of: {
+            AstAddressOf* aof = (AstAddressOf *) expr;
+
+            if (node_is_addressable_literal((AstNode *) aof->expr)) {
+                aof->expr->flags |= Ast_Flag_Decl_Followed_By_Init;
+                aof->expr->flags |= Ast_Flag_Address_Taken;
+                emit_local_allocation(mod, &code, aof->expr);
+                emit_location(mod, &code, aof->expr);
+                emit_expression(mod, &code, aof->expr);
+                emit_store_instruction(mod, &code, aof->expr->type, 0);
+            }
+
+            emit_location(mod, &code, aof->expr);
+            break;
+        }
+
+        case Ast_Kind_Dereference: {
+            AstDereference* deref = (AstDereference *) expr;
+            emit_expression(mod, &code, deref->expr);
+            emit_load_instruction(mod, &code, deref->type, 0);
+            break;
+        }
+
+        case Ast_Kind_Subscript: {
+            AstSubscript* sub = (AstSubscript *) expr;
+            u64 offset = 0;
+            emit_subscript_location(mod, &code, sub, &offset);
+            emit_load_instruction(mod, &code, sub->type, offset);
+            break;
+        }
+
+        case Ast_Kind_Field_Access: {
+            AstFieldAccess* field = (AstFieldAccess* ) expr;
+
+            if (field->expr->kind == Ast_Kind_Param) {
+                if (type_get_param_pass(field->expr->type) == Param_Pass_By_Value && !type_is_pointer(field->expr->type)) {
+                    u64 localidx = bh_imap_get(&mod->local_map, (u64) field->expr) + field->idx;
+                    assert(localidx & LOCAL_IS_WASM);
+                    WIL(NULL, WI_LOCAL_GET, localidx);
+                    break;
+                }
+            }
+
+            if (is_lval((AstNode *) field->expr) || type_is_pointer(field->expr->type)) {
+                u64 offset = 0;
+                emit_field_access_location(mod, &code, field, &offset);
+                emit_load_instruction(mod, &code, field->type, offset);
+
+            } else {
+                emit_expression(mod, &code, field->expr);
+
+                i32 idx = type_get_idx_of_linear_member_with_offset(field->expr->type, field->offset);
+                i32 field_linear_members = type_linear_member_count(field->type);
+                i32 total_linear_members = type_linear_member_count(field->expr->type);
+
+                if (idx == 0) {
+                    // Easy case: the member is the first one and all other members just have to be dropped.
+                    fori (i, 0, total_linear_members - field_linear_members) WI(NULL, WI_DROP);
+
+                } else {
+                    // Tough case: Stack shuffling to make the member the only thing on the stack.
+                    // This is very similar to the compound_load/compound_store procedures but it is different enough
+                    // that I cannot find a good way to factor them all without just introducing a ton of complexity.
+                    fori (i, 0, total_linear_members - idx - field_linear_members) WI(NULL, WI_DROP);
+
+                    u64 *temporaries = bh_alloc_array(global_scratch_allocator, u64, field_linear_members);
+                    fori (i, 0, field_linear_members) temporaries[i] = 0;
+
+                    TypeWithOffset two = { 0 };
+                    forir (i, field_linear_members - 1, 0) {
+                        type_linear_member_lookup(field->type, i, &two);
+
+                        WasmType wt = onyx_type_to_wasm_type(two.type);
+                        temporaries[i] = local_raw_allocate(mod->local_alloc, wt);
+                        WIL(NULL, WI_LOCAL_SET, temporaries[i]);
+                    }
+
+                    fori (i, 0, idx) WI(NULL, WI_DROP);
+
+                    fori (i, 0, field_linear_members) {
+                        type_linear_member_lookup(field->type, i, &two);
+
+                        WIL(NULL, WI_LOCAL_GET, temporaries[i]);
+
+                        WasmType wt = onyx_type_to_wasm_type(two.type);
+                        local_raw_free(mod->local_alloc, wt);
+                    }
+
+                    bh_free(global_scratch_allocator, temporaries);
+                }
+            }
+
+            break;
+        }
+
+        case Ast_Kind_Slice: {
+            AstSubscript* sl = (AstSubscript *) expr;
+
+            emit_expression(mod, &code, sl->expr);
+
+            u64 lo_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
+            u64 hi_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
+
+            WI(NULL, WI_DROP);
+            WIL(NULL, WI_LOCAL_SET, hi_local);
+            WIL(NULL, WI_LOCAL_TEE, lo_local);
+            if (sl->elem_size != 1) {
+                WID(NULL, WI_I32_CONST, sl->elem_size);
+                WI(NULL, WI_I32_MUL);
+            }
+            emit_expression(mod, &code, sl->addr);
+            WI(NULL, WI_I32_ADD);
+            WIL(NULL, WI_LOCAL_GET, hi_local);
+            WIL(NULL, WI_LOCAL_GET, lo_local);
+            WI(NULL, WI_I32_SUB);
+
+            local_raw_free(mod->local_alloc, lo_local);
+            local_raw_free(mod->local_alloc, hi_local);
+            break;
+        }
+
+        case Ast_Kind_Size_Of: {
+            AstSizeOf* so = (AstSizeOf *) expr;
+            WID(NULL, WI_I32_CONST, so->size);
+            break;
+        }
+
+        case Ast_Kind_Align_Of: {
+            AstAlignOf* ao = (AstAlignOf *) expr;
+            WID(NULL, WI_I32_CONST, ao->alignment);
+            break;
+        }
+
+        case Ast_Kind_Enum_Value: {
+            AstEnumValue* ev = (AstEnumValue *) expr;
+            AstNumLit   * num = (AstNumLit *) ev->value;
+            assert(num->kind == Ast_Kind_NumLit);
+
+            WasmType backing_type = onyx_type_to_wasm_type(ev->type);
+            if      (backing_type == WASM_TYPE_INT32) WID(NULL, WI_I32_CONST, num->value.i);
+            else if (backing_type == WASM_TYPE_INT64) WID(NULL, WI_I64_CONST, num->value.l);
+            else onyx_report_error(ev->token->pos, Error_Critical, "Invalid backing type for enum.");
+            break;
+        }
+
+        case Ast_Kind_Memres: {
+            AstMemRes* memres = (AstMemRes *) expr;
+            emit_memory_reservation_location(mod, &code, memres);
+            emit_load_instruction(mod, &code, memres->type, 0);
+            break;
+        }
+
+        case Ast_Kind_File_Contents: {
+            AstFileContents* fc = (AstFileContents *) expr;
+
+            assert(fc->data_id > 0);
+            assert(fc->size > 0);
+
+            // :ProperLinking
+            emit_data_relocation(mod, &code, fc->data_id);
+            WID(NULL, WI_I32_CONST, fc->size);
+            break;
+        }
+
+        case Ast_Kind_Compound: {
+            AstCompound* compound = (AstCompound *) expr;
+
+            bh_arr_each(AstTyped *, expr, compound->exprs) {
+                emit_expression(mod, &code, *expr);
+            }
+            break;
+        }
+
+        case Ast_Kind_Call_Site: {
+            AstCallSite* callsite = (AstCallSite *) expr;
+
+            emit_expression(mod, &code, (AstTyped *) callsite->filename);
+            emit_expression(mod, &code, (AstTyped *) callsite->line);
+            emit_expression(mod, &code, (AstTyped *) callsite->column);
+            break;
+        }
+
+        case Ast_Kind_If_Expression: {
+            AstIfExpression* if_expr = (AstIfExpression *) expr;
+            emit_if_expression(mod, &code, if_expr);
+            break;
+        }
+
+        case Ast_Kind_Switch_Case: {
+            // This error message should be moved to checking, but this is the
+            // best place to do it right now.
+            onyx_report_error(expr->token->pos, Error_Critical, "'case' statements are only allowed in a 'switch' statement.");
+            break;
+        }
+
+        case Ast_Kind_Code_Block: {
+            // Like above, this error message should be moved to checking, but
+            // this is the best place to do it right now.
+            onyx_report_error(expr->token->pos, Error_Critical, "'#quote' blocks are only to be used at compile-time. Using them as a runtime value is not allowed.");
+            break;
+        }
+
+        case Ast_Kind_Foreign_Block: {
+            AstForeignBlock *fb = (AstForeignBlock *) expr;
+            WID(NULL, WI_I32_CONST, fb->foreign_block_number);
+            break;
+        }
+
+        case Ast_Kind_Zero_Value: {
+            AstZeroValue *zv = (AstZeroValue *) expr;
+            assert(zv->type);
+            emit_zero_value_for_type(mod, &code, zv->type, zv->token);
+            break;
+        }
+
+        default:
+            bh_printf("Unhandled case: %d\n", expr->kind);
+            DEBUG_HERE;
+            assert(0);
+    }
+
+    if ((expr->flags & Ast_Flag_Expr_Ignored) != 0 && !type_results_in_void(expr->type)) {
+        i32 mem_count = 1;
+        if (type_is_compound(expr->type)) mem_count = type_linear_member_count(expr->type);
+        fori (i, 0, mem_count) WI(NULL, WI_DROP);
+    }
+
+    *pcode = code;
+}
+
+static const WasmInstructionType cast_map[][12] = {
+    //          I8               U8                  I16                 U16                I32                 U32                I64                U64                F32                F64                PTR
+    /* I8  */ { WI_NOP,          WI_NOP,             WI_I32_EXTEND_8_S,  WI_NOP,            WI_I32_EXTEND_8_S,  WI_NOP,            WI_I64_FROM_I32_S, WI_I64_FROM_I32_S, WI_UNREACHABLE,    WI_UNREACHABLE,    WI_UNREACHABLE,  WI_UNREACHABLE },
+    /* U8  */ { WI_NOP,          WI_NOP,             WI_NOP,             WI_NOP,            WI_NOP,             WI_NOP,            WI_I64_FROM_I32_U, WI_I64_FROM_I32_U, WI_UNREACHABLE,    WI_UNREACHABLE,    WI_UNREACHABLE,  WI_UNREACHABLE },
+    /* I16 */ { WI_NOP,          WI_NOP,             WI_NOP,             WI_NOP,            WI_I32_EXTEND_16_S, WI_NOP,            WI_I64_FROM_I32_S, WI_I64_FROM_I32_S, WI_UNREACHABLE,    WI_UNREACHABLE,    WI_UNREACHABLE,  WI_UNREACHABLE },
+    /* U16 */ { WI_NOP,          WI_NOP,             WI_NOP,             WI_NOP,            WI_NOP,             WI_NOP,            WI_I64_FROM_I32_U, WI_I64_FROM_I32_U, WI_UNREACHABLE,    WI_UNREACHABLE,    WI_UNREACHABLE,  WI_UNREACHABLE },
+    /* I32 */ { WI_NOP,          WI_NOP,             WI_NOP,             WI_NOP,            WI_NOP,             WI_NOP,            WI_I64_FROM_I32_S, WI_I64_FROM_I32_S, WI_F32_FROM_I32_S, WI_F64_FROM_I32_S, WI_NOP,          WI_NOP         },
+    /* U32 */ { WI_NOP,          WI_NOP,             WI_NOP,             WI_NOP,            WI_NOP,             WI_NOP,            WI_I64_FROM_I32_U, WI_I64_FROM_I32_U, WI_F32_FROM_I32_U, WI_F64_FROM_I32_U, WI_NOP,          WI_NOP         },
+    /* I64 */ { WI_I32_FROM_I64, WI_I32_FROM_I64,    WI_I32_FROM_I64,    WI_I32_FROM_I64,   WI_I32_FROM_I64,    WI_I32_FROM_I64,   WI_NOP,            WI_NOP,            WI_F32_FROM_I64_S, WI_F64_FROM_I64_S, WI_I32_FROM_I64, WI_UNREACHABLE },
+    /* U64 */ { WI_I32_FROM_I64, WI_I32_FROM_I64,    WI_I32_FROM_I64,    WI_I32_FROM_I64,   WI_I32_FROM_I64,    WI_I32_FROM_I64,   WI_NOP,            WI_NOP,            WI_F32_FROM_I64_U, WI_F64_FROM_I64_U, WI_I32_FROM_I64, WI_UNREACHABLE },
+    /* F32 */ { WI_UNREACHABLE,  WI_UNREACHABLE,     WI_UNREACHABLE,     WI_UNREACHABLE,    WI_I32_FROM_F32_S,  WI_I32_FROM_F32_U, WI_I64_FROM_F32_S, WI_I64_FROM_F32_U, WI_NOP,            WI_F64_FROM_F32,   WI_UNREACHABLE,  WI_UNREACHABLE },
+    /* F64 */ { WI_UNREACHABLE,  WI_UNREACHABLE,     WI_UNREACHABLE,     WI_UNREACHABLE,    WI_I32_FROM_F64_S,  WI_I32_FROM_F64_U, WI_I64_FROM_F64_S, WI_I64_FROM_F64_U, WI_F32_FROM_F64,   WI_NOP,            WI_UNREACHABLE,  WI_UNREACHABLE },
+    /* PTR */ { WI_UNREACHABLE,  WI_UNREACHABLE,     WI_UNREACHABLE,     WI_UNREACHABLE,    WI_NOP,             WI_NOP,            WI_I64_FROM_I32_U, WI_I64_FROM_I32_U, WI_UNREACHABLE,    WI_UNREACHABLE,    WI_NOP,          WI_UNREACHABLE },
+    /* TYP */ { WI_UNREACHABLE,  WI_UNREACHABLE,     WI_UNREACHABLE,     WI_UNREACHABLE,    WI_NOP,             WI_NOP,            WI_UNREACHABLE,    WI_UNREACHABLE,    WI_UNREACHABLE,    WI_UNREACHABLE,    WI_UNREACHABLE,  WI_NOP         },
+};
+
+EMIT_FUNC(cast, AstUnaryOp* cast) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    emit_expression(mod, &code, cast->expr);
+
+    Type* from = cast->expr->type;
+    Type* to = cast->type;
+    if (from->kind == Type_Kind_Enum) from = from->Enum.backing;
+    if (to->kind == Type_Kind_Enum) to = to->Enum.backing;
+
+    if (type_is_simd(from) && type_is_simd(to)) {
+        *pcode = code;
+        return;
+    }
+
+    if (to->kind == Type_Kind_Basic && to->Basic.kind == Basic_Kind_Void) {
+        WI(NULL, WI_DROP);
+        *pcode = code;
+        return;
+    }
+
+    if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_Array) {
+        WID(NULL, WI_I32_CONST, from->Array.count);
+        *pcode = code;
+        return;
+    }
+
+    if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_DynArray) {
+        WI(NULL, WI_DROP);
+        WI(NULL, WI_DROP);
+        WI(NULL, WI_DROP);
+        *pcode = code;
+        return;
+    }
+
+    if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_Slice) {
+        // Nothing needs to be done because they are identical
+        *pcode = code;
+        return;
+    }
+
+    if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_VarArgs) {
+        // Nothing needs to be done because they are identical
+        *pcode = code;
+        return;
+    }
+
+    if (to->kind == Type_Kind_Distinct || from->kind == Type_Kind_Distinct) {
+        // Nothing needs to be done because they are identical
+        *pcode = code;
+        return;
+    }
+
+    i32 fromidx = -1, toidx = -1;
+    if (from->Basic.flags & Basic_Flag_Pointer || from->kind == Type_Kind_Array) {
+        fromidx = 10;
+    }
+    else if (from->Basic.flags & Basic_Flag_Integer) {
+        b32 unsign = (from->Basic.flags & Basic_Flag_Unsigned) != 0;
+
+        fromidx = log2_dumb(from->Basic.size) * 2 + unsign;
+    }
+    else if (from->Basic.flags & Basic_Flag_Float) {
+        if      (from->Basic.size == 4) fromidx = 8;
+        else if (from->Basic.size == 8) fromidx = 9;
+    }
+    else if (from->Basic.flags & Basic_Flag_Boolean) {
+        fromidx = 0;
+    }
+    else if (from->Basic.flags & Basic_Flag_Type_Index) {
+        fromidx = 11;
+    }
+
+    if (to->Basic.flags & Basic_Flag_Pointer || to->kind == Type_Kind_Array) {
+        toidx = 10;
+    }
+    else if (to->Basic.flags & Basic_Flag_Integer) {
+        b32 unsign = (to->Basic.flags & Basic_Flag_Unsigned) != 0;
+
+        toidx = log2_dumb(to->Basic.size) * 2 + unsign;
+    }
+    else if (to->Basic.flags & Basic_Flag_Float) {
+        if      (to->Basic.size == 4) toidx = 8;
+        else if (to->Basic.size == 8) toidx = 9;
+    }
+    else if (to->Basic.flags & Basic_Flag_Boolean) {
+        toidx = 0;
+    }
+    else if (to->Basic.flags & Basic_Flag_Type_Index) {
+        toidx = 11;
+    }
+
+    if (fromidx != -1 && toidx != -1) {
+        WasmInstructionType cast_op = cast_map[fromidx][toidx];
+        assert(cast_op != WI_UNREACHABLE);
+
+        if (cast_op != WI_NOP) {
+            WI(NULL, cast_op);
+        }
+    }
+
+    *pcode = code;
+}
+
+EMIT_FUNC(return, AstReturn* ret) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    // If we have an expression to return, we see if it should be placed on the linear memory stack, or the WASM stack.
+    if (ret->expr) {
+        if (bh_arr_length(mod->return_location_stack) > 0) {
+            AstLocal* dest = bh_arr_last(mod->return_location_stack);
+            u64 dest_loc = bh_imap_get(&mod->local_map, (u64) dest);
+            b32 dest_is_local = (b32) ((dest_loc & LOCAL_IS_WASM) != 0);
+
+            u64 offset = 0;
+            if (!dest_is_local) emit_local_location(mod, &code, dest, &offset);
+
+            emit_expression(mod, &code, ret->expr);
+
+            if (!dest_is_local) emit_store_instruction(mod, &code, dest->type, offset);
+            else                WIL(NULL, WI_LOCAL_SET, dest_loc);
+
+        } else if (mod->curr_cc == CC_Return_Stack) {
+            WIL(NULL, WI_LOCAL_GET, mod->stack_base_idx);
+            WID(NULL, WI_I32_CONST, type_size_of(ret->expr->type));
+            WI(NULL, WI_I32_SUB);
+
+            emit_expression(mod, &code, ret->expr);
+            emit_store_instruction(mod, &code, ret->expr->type, 0);
+
+        } else {
+            emit_expression(mod, &code, ret->expr);
+        }
+    }
+
+    // Clear the normal deferred statements
+    emit_deferred_stmts(mod, &code);
+
+    i64 jump_label = get_structured_jump_label(mod, Jump_Type_Return, 1);
+    if (jump_label >= 0) {
+        WIL(NULL, WI_JUMP, jump_label);
+
+    } else {
+        // Clear the rest of the deferred statements
+        if (bh_arr_length(mod->deferred_stmts) > 0) {
+            i32 i = bh_arr_length(mod->deferred_stmts) - 1;
+            while (i >= 0) {
+                emit_deferred_stmt(mod, &code, mod->deferred_stmts[i]);
+                i--;
+            }
+        }
+
+        // Make a patch for the two instructions needed to restore the stack pointer
+        SUBMIT_PATCH(mod->stack_leave_patches, 0);
+        WI(NULL, WI_NOP);
+        WI(NULL, WI_NOP);
+
+        WI(NULL, WI_RETURN);
+    }
+
+    *pcode = code;
+}
+
+EMIT_FUNC(stack_enter, u64 stacksize) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    bh_align(stacksize, 16);
+
+    u64 stack_top_idx = bh_imap_get(&mod->index_map, (u64) &builtin_stack_top);
+
+    // HACK: slightly... There will be space for 5 instructions
+    if (stacksize == 0) {
+        code[0] = (WasmInstruction) { WI_GLOBAL_GET, { .l = stack_top_idx } };
+        code[1] = (WasmInstruction) { WI_LOCAL_SET,  { .l = mod->stack_base_idx} };
+        code[2] = (WasmInstruction) { WI_NOP,        0 };
+        code[3] = (WasmInstruction) { WI_NOP,        0 };
+        code[4] = (WasmInstruction) { WI_NOP,        0 };
+    } else {
+        code[0] = (WasmInstruction) { WI_GLOBAL_GET, { .l = stack_top_idx } };
+        code[1] = (WasmInstruction) { WI_LOCAL_TEE,  { .l = mod->stack_base_idx} };
+        code[2] = (WasmInstruction) { WI_I32_CONST,  { .l = stacksize } };
+        code[3] = (WasmInstruction) { WI_I32_ADD,    0 };
+        code[4] = (WasmInstruction) { WI_GLOBAL_SET, { .l = stack_top_idx } };
+    }
+
+    *pcode = code;
+}
+
+EMIT_FUNC(zero_value, WasmType wt) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    switch (wt) {
+        case WASM_TYPE_INT32:   WIL(NULL, WI_I32_CONST, 0); break;
+        case WASM_TYPE_INT64:   WIL(NULL, WI_I64_CONST, 0); break;
+        case WASM_TYPE_FLOAT32: WIL(NULL, WI_F32_CONST, 0); break;
+        case WASM_TYPE_FLOAT64: WIL(NULL, WI_F64_CONST, 0); break;
+        case WASM_TYPE_VAR128:  {
+            static u8 zero_v128[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+            WIP(NULL, WI_V128_CONST, &zero_v128);
+            break;
+        }
+    }
+
+    *pcode = code;
+}
+
+EMIT_FUNC(zero_value_for_type, Type* type, OnyxToken* where) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    if (type_is_structlike_strict(type)) {
+        i32 mem_count = type_linear_member_count(type);
+        TypeWithOffset two;
+
+        fori (i, 0, mem_count) {
+            type_linear_member_lookup(type, i, &two);
+            emit_zero_value_for_type(mod, &code, two.type, where);
+        }
+    }
+    else if (type->kind == Type_Kind_Function) {
+        WID(NULL, WI_I32_CONST, mod->null_proc_func_idx);
+    }
+    else {
+        WasmType wt = onyx_type_to_wasm_type(type);
+        if (wt == WASM_TYPE_VOID) {
+            onyx_report_error(where->pos, Error_Critical, "Cannot produce a zero-value for this type.");
+        }
+        emit_zero_value(mod, &code, wt);
+    }
+
+    *pcode = code;
+}
+
+static i32 generate_type_idx(OnyxWasmModule* mod, Type* ft) {
+    if (ft->kind != Type_Kind_Function) return -1;
+
+    static char type_repr_buf[128];
+    char* t = type_repr_buf;
+
+    Type** param_type = ft->Function.params;
+    i32 param_count = ft->Function.param_count;
+    i32 params_left = param_count;
+
+    while (params_left-- > 0) {
+        if (type_get_param_pass(*param_type) == Param_Pass_By_Implicit_Pointer) {
+            *(t++) = (char) onyx_type_to_wasm_type(&basic_types[Basic_Kind_Rawptr]);
+
+        }
+        else if (type_is_structlike_strict(*param_type)) {
+            u32 mem_count = type_structlike_mem_count(*param_type);
+            StructMember smem;
+
+            fori (i, 0, mem_count) {
+                type_lookup_member_by_idx(*param_type, i, &smem);
+                *(t++) = (char) onyx_type_to_wasm_type(smem.type);
+            }
+
+            param_count += mem_count - 1;
+
+        } else {
+            *(t++) = (char) onyx_type_to_wasm_type(*param_type);
+        }
+
+        param_type++;
+    }
+    *(t++) = ':';
+
+    // HACK: Slightly: the wasm type for structs has to be 0x00
+    WasmType return_type = onyx_type_to_wasm_type(ft->Function.return_type);
+    if (ft->Function.return_type->kind == Type_Kind_Struct && !type_is_compound(ft->Function.return_type)) {
+        return_type = onyx_type_to_wasm_type(ft->Function.return_type->Struct.linear_members[0].type);
+    }
+
+    *(t++) = (char) return_type;
+    *t = '\0';
+
+    i32 type_idx = 0;
+    i32 index = shgeti(mod->type_map, type_repr_buf);
+    if (index != -1) {
+        type_idx = mod->type_map[index].value;
+    } else {
+        // NOTE: Make a new type
+        WasmFuncType* type = (WasmFuncType*) bh_alloc(mod->allocator, sizeof(WasmFuncType) + sizeof(WasmType) * param_count);
+        type->return_type = return_type;
+        type->param_count = param_count;
+
+        fori (i, 0, type->param_count) {
+            type->param_types[i] = type_repr_buf[i];
+        }
+
+        bh_arr_push(mod->types, type);
+
+        shput(mod->type_map, type_repr_buf, mod->next_type_idx);
+        type_idx = mod->next_type_idx;
+        mod->next_type_idx++;
+    }
+
+    return type_idx;
+}
+
+static i32 get_element_idx(OnyxWasmModule* mod, AstFunction* func) {
+    if (bh_imap_has(&mod->elem_map, (u64) func)) {
+        return bh_imap_get(&mod->elem_map, (u64) func);
+    } else {
+        i32 idx = mod->next_elem_idx;
+        bh_imap_put(&mod->elem_map, (u64) func, idx);
+
+        i32 func_idx = bh_imap_get(&mod->index_map, (u64) func);
+        bh_arr_push(mod->elems, func_idx);
+
+        mod->next_elem_idx++;
+
+        return idx;
+    }
+}
+
+static void emit_function(OnyxWasmModule* mod, AstFunction* fd) {
+    if (!should_emit_function(fd)) return;
+
+    i32 type_idx = generate_type_idx(mod, fd->type);
+
+    WasmFunc wasm_func = { 0 };
+    wasm_func.type_idx = type_idx;
+    wasm_func.location = fd->token;
+
+    bh_arr_new(mod->allocator, wasm_func.code, 16);
+
+    i32 func_idx = (i32) bh_imap_get(&mod->index_map, (u64) fd);
+    mod->current_func_idx = func_idx;
+
+    debug_begin_function(mod, func_idx, fd->token, get_function_name(fd));
+
+    if (fd == builtin_initialize_data_segments && context.options->use_post_mvp_features) {
+        emit_initialize_data_segments_body(mod, &wasm_func.code);
+
+        debug_emit_instruction(mod, NULL);
+        bh_arr_push(wasm_func.code, ((WasmInstruction){ WI_BLOCK_END, 0x00 }));
+
+        bh_arr_set_at(mod->funcs, func_idx - mod->foreign_function_count, wasm_func);
+        mod->current_func_idx = -1;
+
+        debug_end_function(mod);
+        return;
+    }
+
+    if (fd == builtin_run_init_procedures) {
+        emit_run_init_procedures(mod, &wasm_func.code);
+
+        debug_emit_instruction(mod, NULL);
+        bh_arr_push(wasm_func.code, ((WasmInstruction){ WI_BLOCK_END, 0x00 }));
+
+        bh_arr_set_at(mod->funcs, func_idx - mod->foreign_function_count, wasm_func);
+        mod->current_func_idx = -1;
+
+        debug_end_function(mod);
+        return;
+    }
+
+    if (fd->body != NULL) {
+        mod->local_alloc = &wasm_func.locals;
+
+        // NOTE: Generate the local map
+        u64 localidx = 0;
+        bh_arr_each(AstParam, param, fd->params) {
+            switch (type_get_param_pass(param->local->type)) {
+                case Param_Pass_By_Value: {
+                    if (type_is_structlike_strict(param->local->type)) {
+                        debug_introduce_symbol(mod, param->local->token, DSL_REGISTER, localidx | LOCAL_IS_WASM, param->local->type);
+                        bh_imap_put(&mod->local_map, (u64) param->local, localidx | LOCAL_IS_WASM);
+                        localidx += type_structlike_mem_count(param->local->type);
+
+                        break;
+                    }
+                    // fallthrough
+                }
+
+                case Param_Pass_By_Implicit_Pointer: {
+                    debug_introduce_symbol(mod, param->local->token, DSL_REGISTER, localidx | LOCAL_IS_WASM, param->local->type);
+                    bh_imap_put(&mod->local_map, (u64) param->local, localidx++ | LOCAL_IS_WASM);
+                    break;
+                }
+
+                default: assert(0);
+            }
+        }
+
+        mod->local_alloc->param_count = localidx;
+
+        mod->curr_cc = type_function_get_cc(fd->type);
+        assert(mod->curr_cc != CC_Undefined);
+
+        bh_arr_clear(mod->stack_leave_patches);
+
+        debug_emit_instruction(mod, fd->token);
+        debug_emit_instruction(mod, fd->token);
+        debug_emit_instruction(mod, fd->token);
+        debug_emit_instruction(mod, fd->token);
+        debug_emit_instruction(mod, fd->token);
+        bh_arr_insert_end(wasm_func.code, 5);
+        fori (i, 0, 5) wasm_func.code[i] = (WasmInstruction) { WI_NOP, 0 };
+
+        // TODO: Emit debug info for the above instructions
+
+        mod->stack_base_idx = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+
+        // Generate code
+        emit_function_body(mod, &wasm_func.code, fd);
+
+        if (mod->local_alloc->max_stack > 0 || mod->curr_cc == CC_Return_Stack) {
+            emit_stack_enter(mod, &wasm_func.code, mod->local_alloc->max_stack);
+
+            // Place all stack leaves in patch locations. These will (probably) all be
+            // right before a "return" instruction.
+            debug_emit_instruction(mod, NULL);
+            debug_emit_instruction(mod, NULL);
+
+            u64 stack_top_idx = bh_imap_get(&mod->index_map, (u64) &builtin_stack_top);
+            bh_arr_push(wasm_func.code, ((WasmInstruction) { WI_LOCAL_GET,  { .l = mod->stack_base_idx } }));
+            bh_arr_push(wasm_func.code, ((WasmInstruction) { WI_GLOBAL_SET, { .l = stack_top_idx } }));
+
+            bh_arr_each(PatchInfo, patch, mod->stack_leave_patches) {
+                wasm_func.code[patch->instruction_index + 0] = (WasmInstruction) { WI_LOCAL_GET,  { .l = mod->stack_base_idx } };
+                wasm_func.code[patch->instruction_index + 1] = (WasmInstruction) { WI_GLOBAL_SET, { .l = stack_top_idx } };
+            }
+        }
+    }
+
+    WasmFuncType* ft = mod->types[type_idx];
+    emit_zero_value(mod, &wasm_func.code, ft->return_type);
+
+    if (fd->closing_brace) {
+        debug_emit_instruction(mod, fd->closing_brace);
+        bh_arr_push(wasm_func.code, ((WasmInstruction){ WI_NOP, 0x00 }));
+    }
+
+    debug_emit_instruction(mod, NULL);
+    bh_arr_push(wasm_func.code, ((WasmInstruction){ WI_BLOCK_END, 0x00 }));
+
+    bh_imap_clear(&mod->local_map);
+
+    bh_arr_set_at(mod->funcs, func_idx - mod->foreign_function_count, wasm_func);
+    mod->current_func_idx = -1;
+
+    debug_end_function(mod);
+}
+
+static void emit_foreign_function(OnyxWasmModule* mod, AstFunction* fd) {
+    if (!should_emit_function(fd)) return;
+
+    i32 type_idx = generate_type_idx(mod, fd->type);
+
+    WasmImport import = {
+        .kind = WASM_FOREIGN_FUNCTION,
+        .idx  = type_idx,
+        .mod  = bh_aprintf(global_heap_allocator, "%b", fd->foreign_module->text, fd->foreign_module->length),
+        .name = bh_aprintf(global_heap_allocator, "%b", fd->foreign_name->text, fd->foreign_name->length),
+    };
+
+    bh_arr_push(mod->imports, import);
+    return;
+}
+
+static void emit_export_directive(OnyxWasmModule* mod, AstDirectiveExport* export) {
+    assert(export->export_name);
+    assert(export->export);
+
+    token_toggle_end(export->export_name);
+
+    i64 idx = bh_imap_get(&mod->index_map, (u64) export->export);
+
+    WasmExport wasm_export;
+    wasm_export.idx = (i32) idx;
+
+    switch (export->export->kind) {
+        case Ast_Kind_Function: wasm_export.kind = WASM_FOREIGN_FUNCTION;
+                                break;
+
+        case Ast_Kind_Global:   wasm_export.kind = WASM_FOREIGN_GLOBAL;
+                                break;
+    }
+
+    shput(mod->exports, export->export_name->text, wasm_export);
+    mod->export_count++;
+
+    token_toggle_end(export->export_name);
+    return;
+}
+
+static void emit_global(OnyxWasmModule* module, AstGlobal* global) {
+    WasmType global_type = onyx_type_to_wasm_type(global->type);
+
+    WasmGlobal glob = {
+        .type = global_type,
+        .mutable = (global->flags & Ast_Flag_Const) == 0,
+        .initial_value = NULL,
+    };
+
+    i32 global_idx = (i32) bh_imap_get(&module->index_map, (u64) global);
+
+    bh_arr_new(global_heap_allocator, glob.initial_value, 1);
+
+    switch (global_type) {
+        case WASM_TYPE_INT32:   bh_arr_push(glob.initial_value, ((WasmInstruction) { WI_I32_CONST, 0 })); break;
+        case WASM_TYPE_INT64:   bh_arr_push(glob.initial_value, ((WasmInstruction) { WI_I64_CONST, 0 })); break;
+        case WASM_TYPE_FLOAT32: bh_arr_push(glob.initial_value, ((WasmInstruction) { WI_F32_CONST, 0 })); break;
+        case WASM_TYPE_FLOAT64: bh_arr_push(glob.initial_value, ((WasmInstruction) { WI_F64_CONST, 0 })); break;
+
+        default: assert(("Invalid global type", 0)); break;
+    }
+
+    bh_arr_set_at(module->globals, global_idx, glob);
+
+    if (global == &builtin_stack_top)
+        module->stack_top_ptr = &module->globals[global_idx].initial_value[0].data.i1;
+
+    if (global == &builtin_heap_start)
+        module->heap_start_ptr = &module->globals[global_idx].initial_value[0].data.i1;
+
+    if (global == &builtin_tls_size)
+        module->globals[global_idx].initial_value[0].data.i1 =  module->next_tls_offset;
+}
+
+static void emit_string_literal(OnyxWasmModule* mod, AstStrLit* strlit) {
+
+    // NOTE: Allocating more than necessary, but there are no cases
+    // in a string literal that create more bytes than already
+    // existed. You can create less however ('\n' => 0x0a).
+    i8* strdata = bh_alloc_array(global_heap_allocator, i8, strlit->token->length + 1);
+    i32 length  = string_process_escape_seqs(strdata, strlit->token->text, strlit->token->length);
+
+    i32 index = shgeti(mod->string_literals, (char *) strdata);
+    if (index != -1) {
+        StrLitInfo sti = mod->string_literals[index].value;
+        strlit->data_id = sti.data_id;
+        strlit->length  = sti.len;
+
+        bh_free(global_heap_allocator, strdata);
+        return;
+    }
+    
+    // :ProperLinking
+    u32 actual_length = length + (strlit->is_cstr ? 1 : 0);
+    WasmDatum datum = {
+        .alignment = 1,
+        .length = actual_length,
+        .data = strdata,
+    };
+
+    strlit->data_id = emit_data_entry(mod, &datum);
+    strlit->length  = length;
+
+    // :ProperLinking
+    shput(mod->string_literals, (char *) strdata, ((StrLitInfo) { strlit->data_id, strlit->length }));
+}
+
+static u32 emit_data_entry(OnyxWasmModule *mod, WasmDatum *datum) {
+    datum->offset_ = 0;
+    datum->id = NEXT_DATA_ID(mod); 
+    bh_arr_push(mod->data, *datum);
+    return datum->id;
+}
+
+static void emit_constexpr(ConstExprContext *ctx, AstTyped *node, u32 offset) {
+    if (!emit_constexpr_(ctx, node, offset)) {
+        onyx_report_error(node->token->pos, Error_Critical,
+            "Cannot generate constant data for '%s'.",
+            onyx_ast_node_kind_string(node->kind));
+    }
+}
+
+static b32 emit_constexpr_(ConstExprContext *ctx, AstTyped *node, u32 offset) {
+#define CE(type, off) (*((type *) bh_pointer_add(ctx->data, offset + (off))))
+    assert(ctx->data_id);
+    assert(ctx->data);
+
+    b32 retval = 1;
+    node = (AstTyped *) strip_aliases((AstNode *) node);
+
+    if (node_is_type((AstNode *) node)) {
+        Type* constructed_type = type_build_from_ast(context.ast_alloc, (AstType *) node);
+        CE(i32, 0) = constructed_type->id;
+        return 1;
+    }
+
+    switch (node->kind) {
+    case Ast_Kind_Array_Literal: {
+        AstArrayLiteral* al = (AstArrayLiteral *) node;
+
+        i32 i = 0;
+        i32 elem_size = type_size_of(al->type->Array.elem);
+
+        bh_arr_each(AstTyped *, expr, al->values) {
+            retval &= emit_constexpr_(ctx, *expr, i * elem_size + offset);
+            i++;
+        }
+
+        break;
+    }
+
+    case Ast_Kind_Struct_Literal: {
+        AstStructLiteral* sl = (AstStructLiteral *) node;
+
+        Type* sl_type = sl->type;
+        // ROBUSTNESS: Handle cases for slices and dynamic arrays
+        if (sl_type->kind != Type_Kind_Struct && sl_type->kind != Type_Kind_Slice && sl_type->kind != Type_Kind_DynArray) {
+            retval = 0;
+            break;
+        }
+
+        i32 mem_count = type_structlike_mem_count(sl_type);
+        StructMember smem;
+
+        fori (i, 0, mem_count) {
+            type_lookup_member_by_idx(sl_type, i, &smem);
+            retval &= emit_constexpr_(ctx, sl->args.values[i], smem.offset + offset);
+        }
+
+        break;
+    }
+
+    case Ast_Kind_StrLit: {
+        AstStrLit* sl = (AstStrLit *) node;
+
+        // NOTE: This assumes the data_id and the length fields have been filled out
+        // by emit_string_literal.
+        if (POINTER_SIZE == 4) {
+            CE(u32, 0) = 0;
+            CE(u32, 4) = sl->length;
+        } else {
+            CE(u64, 0) = 0;
+            CE(u64, 8) = sl->length;
+        }
+
+        assert(sl->data_id > 0);
+
+        DatumPatchInfo patch;
+        patch.kind = Datum_Patch_Data;
+        patch.index = ctx->data_id;
+        patch.location = offset;
+        patch.data_id = sl->data_id;
+        patch.offset = 0;
+        bh_arr_push(ctx->module->data_patches, patch);
+
+        break;
+    }
+
+    case Ast_Kind_Enum_Value: {
+        AstEnumValue* ev = (AstEnumValue *) node;
+        retval &= emit_constexpr_(ctx, (AstTyped *) ev->value, offset);
+        break;
+    }
+
+    case Ast_Kind_Function: {
+        AstFunction* func = (AstFunction *) node;
+        CE(u32, 0) = get_element_idx(ctx->module, func);
+        break;
+    }
+
+    case Ast_Kind_Size_Of: {
+        AstSizeOf* so = (AstSizeOf *) node;
+        CE(u32, 0) = so->size;
+        break;
+    }
+
+    case Ast_Kind_Align_Of: {
+        AstAlignOf* ao = (AstAlignOf *) node;
+        CE(u32, 0) = ao->alignment;
+        break;
+    }
+
+    case Ast_Kind_Zero_Value: {
+        memset(bh_pointer_add(ctx->data, offset), 0, type_size_of(node->type));
+        break;
+    }
+
+    case Ast_Kind_NumLit: {
+        // NOTE: This makes a big assumption that we are running on a
+        // little endian machine, since WebAssembly is little endian
+        // by specification. This is probably a safe assumption, but
+        // should probably be fixed at some point.
+        //                                - brendanfh  2020/12/15
+
+        Type* effective_type = node->type;
+
+        if (effective_type->kind == Type_Kind_Enum)
+            effective_type = effective_type->Enum.backing;
+
+        switch (effective_type->Basic.kind) {
+        case Basic_Kind_Bool:
+        case Basic_Kind_I8:
+        case Basic_Kind_U8:
+            CE(i8, 0) = (i8) ((AstNumLit *) node)->value.i;
+            return retval;
+
+        case Basic_Kind_I16:
+        case Basic_Kind_U16:
+            CE(i16, 0) = (i16) ((AstNumLit *) node)->value.i;
+            return retval;
+
+        case Basic_Kind_I32:
+        case Basic_Kind_U32:
+        case Basic_Kind_Rawptr:
+            CE(i32, 0) = ((AstNumLit *) node)->value.i;
+            return retval;
+
+        case Basic_Kind_I64:
+        case Basic_Kind_U64:
+            CE(i64, 0) = ((AstNumLit *) node)->value.l;
+            return retval;
+
+        case Basic_Kind_F32:
+            CE(f32, 0) = ((AstNumLit *) node)->value.f;
+            return retval;
+
+        case Basic_Kind_F64:
+            CE(f64, 0) = ((AstNumLit *) node)->value.d;
+            return retval;
+
+        default: break;
+        }
+
+        //fallthrough
+    }
+
+    case Ast_Kind_Code_Block: break;
+
+    default: retval = 0;
+    }
+
+    return retval;
+
+#undef CE
+}
+
+static void emit_memory_reservation(OnyxWasmModule* mod, AstMemRes* memres) {
+    // :ProperLinking
+    Type* effective_type = memres->type;
+
+    u64 alignment = type_alignment_of(effective_type);
+    u64 size = type_size_of(effective_type);
+
+    if (type_table_node != NULL && (AstMemRes *) type_table_node == memres) {
+        u64 table_location = build_type_table(mod);
+        memres->data_id = table_location;
+        return;
+    }
+
+    if (foreign_blocks_node != NULL && (AstMemRes *) foreign_blocks_node == memres) {
+        u64 foreign_blocks_location = build_foreign_blocks(mod);
+        memres->data_id = foreign_blocks_location;
+        return;
+    }
+
+    if (tagged_procedures_node != NULL && (AstMemRes *) tagged_procedures_node == memres) {
+        u64 tagged_procedures_location = build_tagged_procedures(mod);
+        memres->data_id = tagged_procedures_location;
+        return;
+    }
+
+    if (memres->threadlocal) {
+        memres->tls_offset = mod->next_tls_offset;
+        bh_align(memres->tls_offset, alignment);
+        mod->next_tls_offset = memres->tls_offset + size;
+
+    } else {
+        // :ProperLinking
+        u8* data = NULL;
+        if (memres->initial_value != NULL) {
+            assert(!memres->threadlocal);
+            data = bh_alloc(global_heap_allocator, size);
+        }
+
+        WasmDatum datum = {
+            .alignment = alignment,
+            .length = size,
+            .data = data,
+        };
+        memres->data_id = emit_data_entry(mod, &datum);
+
+        if (memres->initial_value != NULL) {
+            ConstExprContext constexpr_ctx;
+            constexpr_ctx.module = mod;
+            constexpr_ctx.data = data;
+            constexpr_ctx.data_id = memres->data_id;
+            emit_constexpr(&constexpr_ctx, memres->initial_value, 0);
+        }
+    }
+}
+
+static void emit_file_contents(OnyxWasmModule* mod, AstFileContents* fc) {
+    // INVESTIGATE: I think that filename should always be NULL when this function starts because
+    // a file contents entity is only processed once and therefore should only go through this step
+    // once. But somehow filename isn't NULL occasionally so I have to check for that...
+    //                                                                      - brendanfh  2021/05/23
+    if (fc->filename == NULL) {
+        const char* parent_file = fc->token->pos.filename;
+        if (parent_file == NULL) parent_file = ".";
+
+        char* parent_folder = bh_path_get_parent(parent_file, global_scratch_allocator);
+        
+        OnyxToken *filename_token = fc->filename_expr->token;
+
+        token_toggle_end(filename_token);
+        char* temp_fn     = bh_alloc_array(global_scratch_allocator, char, filename_token->length);
+        i32   temp_fn_len = string_process_escape_seqs(temp_fn, filename_token->text, filename_token->length);
+        char* filename    = bh_lookup_file(temp_fn, parent_folder, "", 0, NULL, 0);
+        fc->filename      = bh_strdup(global_heap_allocator, filename);
+        token_toggle_end(filename_token);
+    }
+
+    i32 index = shgeti(mod->loaded_file_info, fc->filename);
+    if (index != -1) {
+        StrLitInfo info = mod->loaded_file_info[index].value;
+        fc->data_id = info.data_id;
+        fc->size    = info.len;
+        return;
+    }
+
+    if (!bh_file_exists(fc->filename)) {
+        onyx_report_error(fc->token->pos, Error_Critical,
+                "Unable to open file for reading, '%s'.",
+                fc->filename);
+        return;
+    }
+
+    // :RelativeFiles This should be relative to the current directory by default; However,
+    // if the filename is prefixed with a './' or '.\\' then it should be relative to the
+    // file in which is was inclded. The loaded file info above should probably use the full
+    // file path in order to avoid duplicates.
+    bh_file_contents contents = bh_file_read_contents(global_heap_allocator, fc->filename);
+    u8* actual_data = bh_alloc(global_heap_allocator, contents.length + 1);
+    u32 length = contents.length + 1;
+    memcpy(actual_data, contents.data, contents.length);
+    actual_data[contents.length] = 0;
+    bh_file_contents_free(&contents);
+
+    WasmDatum datum = {
+        .alignment = 16,
+        .length = length,
+        .data = actual_data,
+    };
+    fc->data_id = emit_data_entry(mod, &datum);
+    fc->size = length - 1;
+
+    shput(mod->loaded_file_info, fc->filename, ((StrLitInfo) {
+        .data_id = fc->data_id,
+        .len = fc->size,
+    }));
+}
+
+OnyxWasmModule onyx_wasm_module_create(bh_allocator alloc) {
+    OnyxWasmModule module = {
+        .allocator = alloc,
+
+        .type_map = NULL,
+        .next_type_idx = 0,
+        .types = NULL,
+
+        .funcs = NULL,
+        .next_func_idx = 0,
+
+        .exports = NULL,
+        .export_count = 0,
+
+        .imports = NULL,
+
+        .globals = NULL,
+        .next_global_idx = 0,
+
+        .data = NULL,
+        .data_patches = NULL,
+
+        .next_tls_offset = 0,
+        .tls_size_ptr = NULL,
+
+        .elems = NULL,
+        .next_elem_idx = 0,
+
+        .needs_memory_section = 0,
+        .memory_min_size = 0,
+        .memory_max_size = 0,
+
+        .structured_jump_target = NULL,
+        .return_location_stack = NULL,
+        .local_allocations = NULL,
+        .stack_leave_patches = NULL,
+        .deferred_stmts = NULL,
+
+        .heap_start_ptr = NULL,
+
+        .stack_top_ptr = NULL,
+        .stack_base_idx = 0,
+
+        .foreign_function_count = 0,
+
+        .null_proc_func_idx = -1,
+
+        .libraries = NULL,
+        .library_paths = NULL,
+
+        .foreign_blocks = NULL,
+        .next_foreign_block_idx = 0,
+
+        .procedures_with_tags = NULL
+    };
+
+    bh_arena* eid = bh_alloc(global_heap_allocator, sizeof(bh_arena));
+    bh_arena_init(eid, global_heap_allocator, 8196);
+    module.extended_instr_data = eid;
+    module.extended_instr_alloc = bh_arena_allocator(eid);
+
+    bh_arr_new(alloc, module.types, 4);
+    bh_arr_new(alloc, module.funcs, 4);
+    bh_arr_new(alloc, module.imports, 4);
+    bh_arr_new(alloc, module.globals, 4);
+    bh_arr_new(alloc, module.data, 4);
+    bh_arr_new(alloc, module.elems, 4);
+    bh_arr_new(alloc, module.libraries, 4);
+    bh_arr_new(alloc, module.library_paths, 4);
+
+    bh_arr_new(global_heap_allocator, module.return_location_stack, 4);
+    bh_arr_new(global_heap_allocator, module.structured_jump_target, 16);
+    bh_arr_set_length(module.structured_jump_target, 0);
+
+    sh_new_arena(module.type_map);
+    sh_new_arena(module.exports);
+    sh_new_arena(module.loaded_file_info);
+    sh_new_arena(module.string_literals);
+
+    bh_imap_init(&module.index_map, global_heap_allocator, 128);
+    bh_imap_init(&module.local_map, global_heap_allocator, 16);
+    bh_imap_init(&module.elem_map,  global_heap_allocator, 16);
+
+    bh_arr_new(global_heap_allocator, module.deferred_stmts, 4);
+    bh_arr_new(global_heap_allocator, module.local_allocations, 4);
+    bh_arr_new(global_heap_allocator, module.stack_leave_patches, 4);
+    bh_arr_new(global_heap_allocator, module.foreign_blocks, 4);
+    bh_arr_new(global_heap_allocator, module.procedures_with_tags, 4);
+    bh_arr_new(global_heap_allocator, module.data_patches, 4);
+
+#ifdef ENABLE_DEBUG_INFO
+    module.debug_context = bh_alloc_item(context.ast_alloc, DebugContext);
+    module.debug_context->allocator = global_heap_allocator;
+    module.debug_context->next_file_id = 0;
+    module.debug_context->next_sym_id = 0;
+    module.debug_context->last_token = NULL;
+
+    sh_new_arena(module.debug_context->file_info);
+    bh_arr_new(global_heap_allocator, module.debug_context->sym_info, 32);
+    bh_arr_new(global_heap_allocator, module.debug_context->sym_patches, 32);
+    bh_arr_new(global_heap_allocator, module.debug_context->funcs, 16);
+
+    bh_buffer_init(&module.debug_context->op_buffer, global_heap_allocator, 1024);
+#endif
+
+    return module;
+}
+
+void emit_entity(Entity* ent) {
+    OnyxWasmModule* module = context.wasm_module;
+    module->current_func_idx = -1;
+
+    switch (ent->type) {
+        case Entity_Type_Foreign_Function_Header:
+            if (!should_emit_function(ent->function)) break;
+
+            module->foreign_function_count++;
+            emit_foreign_function(module, ent->function);
+            // fallthrough
+
+        case Entity_Type_Function_Header:
+            if (!should_emit_function(ent->function)) break;
+
+            if (context.options->print_function_mappings) {
+                bh_printf("%d -> %s:%d:%d\n",
+                    module->next_func_idx,
+                    ent->expr->token->pos.filename,
+                    ent->expr->token->pos.line,
+                    ent->expr->token->pos.column);
+            }
+
+            bh_imap_put(&module->index_map, (u64) ent->function, module->next_func_idx++);
+
+            if (ent->function->flags & Ast_Flag_Proc_Is_Null) {
+                if (module->null_proc_func_idx == -1) module->null_proc_func_idx = get_element_idx(module, ent->function);
+            }
+
+            if (ent->function->tags != NULL) {
+                bh_arr_push(module->procedures_with_tags, ent->function);
+            }
+            break;
+
+        case Entity_Type_Global_Header:
+            bh_imap_put(&module->index_map, (u64) ent->global, module->next_global_idx++);
+            break;
+
+        case Entity_Type_String_Literal: {
+            emit_string_literal(module, (AstStrLit *) ent->strlit);
+            break;
+        }
+
+        case Entity_Type_File_Contents: {
+            emit_file_contents(module, (AstFileContents *) ent->file_contents);
+            break;
+        }
+
+        case Entity_Type_Memory_Reservation: {
+            emit_memory_reservation(module, (AstMemRes *) ent->mem_res);
+            break;
+        }
+
+        case Entity_Type_Process_Directive: {
+            if (ent->expr->kind == Ast_Kind_Directive_Export) {
+                emit_export_directive(module, (AstDirectiveExport *) ent->expr);
+            }
+
+            if (ent->expr->kind == Ast_Kind_Directive_Library) {
+                bh_arr_push(module->libraries, ent->library->library_name);
+            }
+            break;
+        }
+
+        case Entity_Type_Foreign_Block: {
+            ent->foreign_block->foreign_block_number = module->next_foreign_block_idx++;
+            bh_arr_push(module->foreign_blocks, (AstForeignBlock *) ent->foreign_block);
+            break;
+        }
+
+        case Entity_Type_Function: emit_function(module, ent->function); break;
+        case Entity_Type_Global:   emit_global(module,   ent->global); break;
+
+        // Cleanup: Maybe these should be printed elsewhere?
+        // Also, they should be sorted? Or have that ability?
+        case Entity_Type_Note: {
+            if (!context.options->print_notes) break;
+
+            AstNote* note = (AstNote *) ent->expr;
+            OnyxFilePos pos = note->token->pos;
+
+            bh_printf("Note: %b %s:%d:%d\n", note->token->text, note->token->length, pos.filename, pos.line, pos.column);
+
+            break;
+        }
+
+        default: break;
+    }
+
+    ent->state = Entity_State_Finalized;
+}
+
+void onyx_wasm_module_link(OnyxWasmModule *module, OnyxWasmLinkOptions *options) {
+    // If the pointer size is going to change,
+    // the code will probably need to be altered.
+    assert(POINTER_SIZE == 4);
+
+    module->memory_min_size = options->memory_min_size;
+    module->memory_max_size = options->memory_max_size;
+
+    if (context.options->use_multi_threading || options->import_memory) {
+        module->needs_memory_section = 0;
+
+        WasmImport mem_import = {
+            .kind   = WASM_FOREIGN_MEMORY,
+            .min    = options->memory_min_size,
+            .max    = options->memory_max_size, // NOTE: Why not use all 4 Gigs of memory?
+            .shared = context.options->runtime == Runtime_Js,
+
+            .mod    = options->import_memory_module_name,
+            .name   = options->import_memory_import_name,
+        };
+
+        bh_arr_push(module->imports, mem_import);
+
+    } else {
+        module->needs_memory_section = 1;
+    }
+
+    if (options->export_memory) {
+        WasmExport mem_export = {
+            .kind = WASM_FOREIGN_MEMORY,
+            .idx = 0,
+        };
+
+        shput(module->exports, options->export_memory_name, mem_export);
+        module->export_count++;
+    }
+
+    if (options->export_func_table) {
+        WasmExport func_table_export = {
+            .kind = WASM_FOREIGN_TABLE,
+            .idx  = 0,
+        };
+
+        shput(module->exports, options->export_func_table_name, func_table_export);
+        module->export_count++;
+    }
+
+    u32 datum_offset = options->null_reserve_size;
+    bh_arr_each(WasmDatum, datum, module->data) {
+        assert(datum->id > 0);
+
+        bh_align(datum_offset, datum->alignment);
+        datum->offset_ = datum_offset;
+
+        datum_offset += datum->length;
+    }
+
+    bh_arr_each(DatumPatchInfo, patch, module->data_patches) {
+        assert(patch->data_id > 0);
+        WasmDatum *datum = &module->data[patch->data_id - 1];
+        assert(datum->id == patch->data_id);
+
+        switch (patch->kind) {
+            case Datum_Patch_Instruction: {
+                WasmFunc *func = &module->funcs[patch->index - module->foreign_function_count];
+
+                assert(func->code[patch->location].type == WI_PTR_CONST);
+                func->code[patch->location].data.l = (u64) datum->offset_ + patch->offset;
+                break;
+            }
+
+            case Datum_Patch_Data: {
+                WasmDatum *datum_to_alter = &module->data[patch->index - 1];
+                assert(datum_to_alter->id == patch->index);
+
+                *((u32 *) bh_pointer_add(datum_to_alter->data, patch->location)) = (u32) datum->offset_ + patch->offset;
+                break;
+            }
+
+            case Datum_Patch_Relative: {
+                WasmDatum *datum_to_alter = &module->data[patch->index - 1];
+                assert(datum_to_alter->id == patch->index);
+
+                u32 *addr = (u32 *) bh_pointer_add(datum_to_alter->data, patch->location);
+                if (*addr != 0) {
+                    *addr += (u32) datum->offset_ + patch->offset;
+                }
+
+                break;
+            }
+
+            default: assert(0);
+        }
+    }
+
+    assert(module->stack_top_ptr && module->heap_start_ptr);
+
+    *module->stack_top_ptr = datum_offset;
+    bh_align(*module->stack_top_ptr, options->stack_alignment);
+
+    *module->heap_start_ptr = *module->stack_top_ptr + options->stack_size;
+    bh_align(*module->heap_start_ptr, 16);
+}
+
+void onyx_wasm_module_free(OnyxWasmModule* module) {
+    if (module->extended_instr_data != NULL)
+        bh_arena_free(module->extended_instr_data);
+
+    bh_arr_free(module->types);
+    bh_arr_free(module->funcs);
+    bh_imap_free(&module->local_map);
+    bh_imap_free(&module->index_map);
+    shfree(module->type_map);
+    shfree(module->exports);
+}
+
+
+b32 onyx_wasm_build_link_options_from_node(OnyxWasmLinkOptions *opts, AstTyped *node) {
+    node = (AstTyped *) strip_aliases((AstNode *) node);
+
+    assert(node && node->kind == Ast_Kind_Struct_Literal);
+    assert(builtin_link_options_type);
+
+    Type *link_options_type = type_build_from_ast(context.ast_alloc, builtin_link_options_type);
+    
+    AstStructLiteral *input = (AstStructLiteral *) node;
+
+    StructMember smem;
+    b32 out_is_valid;
+
+    // TODO: These should be properly error handled.
+    assert(type_lookup_member(link_options_type, "stack_first", &smem));
+    opts->stack_first = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid) != 0;
+    if (!out_is_valid) return 0;
+
+    assert(type_lookup_member(link_options_type, "stack_size", &smem));
+    opts->stack_size = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid);
+    if (!out_is_valid) return 0;
+
+    assert(type_lookup_member(link_options_type, "stack_alignment", &smem));
+    opts->stack_alignment = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid);
+    if (!out_is_valid) return 0;
+
+    assert(type_lookup_member(link_options_type, "null_reserve_size", &smem));
+    opts->null_reserve_size = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid);
+    if (!out_is_valid) return 0;
+
+    assert(type_lookup_member(link_options_type, "import_memory", &smem));
+    opts->import_memory = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid) != 0;
+    if (!out_is_valid) return 0;
+
+    assert(type_lookup_member(link_options_type, "import_memory_module_name", &smem));
+    opts->import_memory_module_name = get_expression_string_value(input->args.values[smem.idx], &out_is_valid);
+    if (!out_is_valid) return 0;
+
+    assert(type_lookup_member(link_options_type, "import_memory_import_name", &smem));
+    opts->import_memory_import_name = get_expression_string_value(input->args.values[smem.idx], &out_is_valid);
+    if (!out_is_valid) return 0;
+
+    assert(type_lookup_member(link_options_type, "export_memory", &smem));
+    opts->export_memory = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid) != 0;
+    if (!out_is_valid) return 0;
+
+    assert(type_lookup_member(link_options_type, "export_memory_name", &smem));
+    opts->export_memory_name = get_expression_string_value(input->args.values[smem.idx], &out_is_valid);
+    if (!out_is_valid) return 0;
+
+    assert(type_lookup_member(link_options_type, "export_func_table", &smem));
+    opts->export_func_table = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid) != 0;
+    if (!out_is_valid) return 0;
+
+    assert(type_lookup_member(link_options_type, "export_func_table_name", &smem));
+    opts->export_func_table_name = get_expression_string_value(input->args.values[smem.idx], &out_is_valid);
+    if (!out_is_valid) return 0;
+
+    assert(type_lookup_member(link_options_type, "memory_min_size", &smem));
+    opts->memory_min_size = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid);
+    if (!out_is_valid) return 0;
+
+    assert(type_lookup_member(link_options_type, "memory_max_size", &smem));
+    opts->memory_max_size = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid);
+    if (!out_is_valid) return 0;
+
+    return 1;
+}
+
+#include "wasm_output.h"
diff --git a/compiler/src/wasm_intrinsics.h b/compiler/src/wasm_intrinsics.h
new file mode 100644 (file)
index 0000000..49dae8c
--- /dev/null
@@ -0,0 +1,452 @@
+// This file is directly included in src/onxywasm.c
+// It is here purely to decrease the amount of clutter in the main file.
+
+
+// IMPROVE: This implementation assumes that the source and destination buffers do not overlap.
+// The specification for memory.copy in WASM does work even if the buffers overlap.
+// Also, this implementation copies byte-by-byte, which is terrible. It should copy
+// quad word by quad word, and then the additional bytes if the count was not divisible by 8.
+// :32BitPointers
+EMIT_FUNC_NO_ARGS(intrinsic_memory_copy) {
+    bh_arr(WasmInstruction) code = *pcode;
+    
+    // The stack should look like this:
+    //     <count>
+    //     <source>
+    //     <dest>
+    
+    u64 count_local  = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
+    u64 source_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+    u64 dest_local   = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+    
+    WIL(NULL, WI_LOCAL_SET, count_local);
+    WIL(NULL, WI_LOCAL_SET, source_local);
+    WIL(NULL, WI_LOCAL_SET, dest_local);
+    
+    // count is greater than 0
+    WIL(NULL, WI_LOCAL_GET, count_local);
+    WID(NULL, WI_I32_CONST, 0);
+    WI(NULL, WI_I32_GT_S);
+    
+    WID(NULL, WI_IF_START, 0x40);
+    WID(NULL, WI_LOOP_START, 0x40);
+    
+    WIL(NULL, WI_LOCAL_GET, count_local);
+    WID(NULL, WI_I32_CONST, 1);
+    WI(NULL, WI_I32_SUB);
+    WIL(NULL, WI_LOCAL_SET, count_local);
+    
+    WIL(NULL, WI_LOCAL_GET, dest_local);
+    WIL(NULL, WI_LOCAL_GET, count_local);
+    WI(NULL, WI_PTR_ADD);
+    
+    WIL(NULL, WI_LOCAL_GET, source_local);
+    WIL(NULL, WI_LOCAL_GET, count_local);
+    WI(NULL, WI_PTR_ADD);
+    
+    WID(NULL, WI_I32_LOAD_8_U, ((WasmInstructionData) { 0, 0 }));
+    WID(NULL, WI_I32_STORE_8, ((WasmInstructionData) { 0, 0 }));
+    
+    WIL(NULL, WI_LOCAL_GET, count_local);
+    WID(NULL, WI_I32_CONST, 0);
+    WI(NULL, WI_I32_GT_S);
+    WID(NULL, WI_COND_JUMP, 0x00);
+    
+    WI(NULL, WI_LOOP_END);
+    WI(NULL, WI_IF_END);
+    
+    local_raw_free(mod->local_alloc, WASM_TYPE_INT32);
+    local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+    local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+    
+    *pcode = code;
+}
+
+EMIT_FUNC_NO_ARGS(intrinsic_memory_fill) {
+    bh_arr(WasmInstruction) code = *pcode;
+    
+    // The stack should look like this:
+    //     <count>
+    //     <byte>
+    //     <dest>
+    
+    u64 count_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
+    u64 byte_local  = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
+    u64 dest_local  = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+    
+    WIL(NULL, WI_LOCAL_SET, count_local);
+    WIL(NULL, WI_LOCAL_SET, byte_local);
+    WIL(NULL, WI_LOCAL_SET, dest_local);
+    
+    // count is greater than 0
+    WIL(NULL, WI_LOCAL_GET, count_local);
+    WID(NULL, WI_I32_CONST, 0);
+    WI(NULL, WI_I32_GT_S);
+    
+    WID(NULL, WI_IF_START, 0x40);
+    WID(NULL, WI_LOOP_START, 0x40);
+    
+    WIL(NULL, WI_LOCAL_GET, count_local);
+    WID(NULL, WI_I32_CONST, 1);
+    WI(NULL, WI_I32_SUB);
+    WIL(NULL, WI_LOCAL_SET, count_local);
+    
+    WIL(NULL, WI_LOCAL_GET, dest_local);
+    WIL(NULL, WI_LOCAL_GET, count_local);
+    WI(NULL, WI_PTR_ADD);
+    
+    WIL(NULL, WI_LOCAL_GET, byte_local);
+    WID(NULL, WI_I32_STORE_8, ((WasmInstructionData) { 0, 0 }));
+    
+    WIL(NULL, WI_LOCAL_GET, count_local);
+    WID(NULL, WI_I32_CONST, 0);
+    WI(NULL, WI_I32_GT_S);
+    WID(NULL, WI_COND_JUMP, 0x00);
+    
+    WI(NULL, WI_LOOP_END);
+    WI(NULL, WI_IF_END);
+    
+    local_raw_free(mod->local_alloc, WASM_TYPE_INT32);
+    local_raw_free(mod->local_alloc, WASM_TYPE_INT32);
+    local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+    
+    *pcode = code;
+}
+
+EMIT_FUNC(initialize_type, Type* type, OnyxToken* where) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    switch (type->kind) {
+        case Type_Kind_Pointer:
+        case Type_Kind_Basic: {
+            WasmType basic_type = onyx_type_to_wasm_type(type);
+            emit_zero_value(mod, &code, basic_type);
+            emit_store_instruction(mod, &code, type, 0);
+            break;
+        }
+
+        case Type_Kind_Struct: {
+            u64 value_ptr = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+            WIL(NULL, WI_LOCAL_SET, value_ptr);
+
+            bh_arr_each(StructMember *, psmem, type->Struct.memarr) {
+                StructMember* smem = *psmem;
+                if (smem->initial_value == NULL || *smem->initial_value == NULL) continue;
+
+                WIL(NULL, WI_LOCAL_GET, value_ptr);
+                emit_expression(mod, &code, *smem->initial_value);
+                emit_store_instruction(mod, &code, smem->type, smem->offset);
+            }
+            
+            local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+            break;
+        }
+
+        default:
+            onyx_report_error(where->pos, Error_Critical,
+                    "Unable to initialize type, '%s'. The reason for this is largely due to the compiler not knowing what the initial value should be.",
+                    type_get_name(type)); 
+            break;
+    }
+
+    *pcode = code;
+}
+
+EMIT_FUNC(intrinsic_atomic_wait, Type* type, OnyxToken* where) {
+    if (type->kind != Type_Kind_Basic) goto bad_type;
+
+    bh_arr(WasmInstruction) code = *pcode;
+
+    switch (type->Basic.kind) {
+        case Basic_Kind_I32:
+        case Basic_Kind_U32: WID(NULL, WI_ATOMIC_WAIT32, ((WasmInstructionData) { 2, 0 })); break;
+
+        case Basic_Kind_I64:
+        case Basic_Kind_U64: WID(NULL, WI_ATOMIC_WAIT64, ((WasmInstructionData) { 3, 0 })); break;
+
+        default: goto bad_type;
+    }
+
+    *pcode = code;
+    return;
+
+bad_type:
+    onyx_report_error(where->pos, Error_Critical, "Bad type for atomic wait, '%s'. Only i32 and i64 are supported.", type_get_name(type));
+}
+
+EMIT_FUNC_NO_ARGS(intrinsic_atomic_notify) {
+    bh_arr(WasmInstruction) code = *pcode;
+    WID(NULL, WI_ATOMIC_NOTIFY, ((WasmInstructionData) { 2, 0 }));
+    *pcode = code;
+}
+
+EMIT_FUNC_NO_ARGS(intrinsic_atomic_fence) {
+    bh_arr(WasmInstruction) code = *pcode;
+    WI(NULL, WI_ATOMIC_FENCE);
+    *pcode = code;
+}
+
+EMIT_FUNC(intrinsic_atomic_load, Type* type, OnyxToken* where) {
+    if (type->kind != Type_Kind_Basic) goto bad_type;
+
+    bh_arr(WasmInstruction) code = *pcode;
+
+    switch (type->Basic.kind) {
+        case Basic_Kind_U8:  WID(NULL, WI_ATOMIC_I32_LOAD8_U, ((WasmInstructionData) { 0, 0 })); break;
+        case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_LOAD16_U, ((WasmInstructionData) { 1, 0 })); break;
+
+        case Basic_Kind_I32:
+        case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_LOAD, ((WasmInstructionData) { 2, 0 })); break;
+
+        case Basic_Kind_I64:
+        case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_LOAD, ((WasmInstructionData) { 3, 0 })); break;
+
+        default: goto bad_type;
+    }
+
+    *pcode = code;
+    return;
+
+bad_type:
+    onyx_report_error(where->pos, Error_Critical, "Bad type for atomic load, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
+}
+
+EMIT_FUNC(intrinsic_atomic_store, Type* type, OnyxToken* where) {
+    if (type->kind != Type_Kind_Basic) goto bad_type;
+
+    bh_arr(WasmInstruction) code = *pcode;
+
+    switch (type->Basic.kind) {
+        case Basic_Kind_U8:  WID(NULL, WI_ATOMIC_I32_STORE8, ((WasmInstructionData) { 0, 0 })); break;
+        case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_STORE16, ((WasmInstructionData) { 1, 0 })); break;
+
+        case Basic_Kind_I32:
+        case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_STORE, ((WasmInstructionData) { 2, 0 })); break;
+
+        case Basic_Kind_I64:
+        case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_STORE, ((WasmInstructionData) { 3, 0 })); break;
+
+        default: goto bad_type;
+    }
+
+    *pcode = code;
+    return;
+
+bad_type:
+    onyx_report_error(where->pos, Error_Critical, "Bad type for atomic store, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
+}
+
+EMIT_FUNC(intrinsic_atomic_add, Type* type, OnyxToken* where) {
+    if (type->kind != Type_Kind_Basic) goto bad_type;
+
+    bh_arr(WasmInstruction) code = *pcode;
+
+    switch (type->Basic.kind) {
+        case Basic_Kind_U8:  WID(NULL, WI_ATOMIC_I32_ADD8_U, ((WasmInstructionData) { 0, 0 })); break;
+        case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_ADD16_U, ((WasmInstructionData) { 1, 0 })); break;
+
+        case Basic_Kind_I32:
+        case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_ADD, ((WasmInstructionData) { 2, 0 })); break;
+
+        case Basic_Kind_I64:
+        case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_ADD, ((WasmInstructionData) { 3, 0 })); break;
+
+        default: goto bad_type;
+    }
+
+    *pcode = code;
+    return;
+
+bad_type:
+    onyx_report_error(where->pos, Error_Critical, "Bad type for atomic add, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
+}
+
+EMIT_FUNC(intrinsic_atomic_sub, Type* type, OnyxToken* where) {
+    if (type->kind != Type_Kind_Basic) goto bad_type;
+
+    bh_arr(WasmInstruction) code = *pcode;
+
+    switch (type->Basic.kind) {
+        case Basic_Kind_U8:  WID(NULL, WI_ATOMIC_I32_SUB8_U, ((WasmInstructionData) { 0, 0 })); break;
+        case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_SUB16_U, ((WasmInstructionData) { 1, 0 })); break;
+
+        case Basic_Kind_I32:
+        case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_SUB, ((WasmInstructionData) { 2, 0 })); break;
+
+        case Basic_Kind_I64:
+        case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_SUB, ((WasmInstructionData) { 3, 0 })); break;
+
+        default: goto bad_type;
+    }
+
+    *pcode = code;
+    return;
+
+bad_type:
+    onyx_report_error(where->pos, Error_Critical, "Bad type for atomic sub, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
+}
+
+EMIT_FUNC(intrinsic_atomic_and, Type* type, OnyxToken* where) {
+    if (type->kind != Type_Kind_Basic) goto bad_type;
+
+    bh_arr(WasmInstruction) code = *pcode;
+
+    switch (type->Basic.kind) {
+        case Basic_Kind_U8:  WID(NULL, WI_ATOMIC_I32_AND8_U, ((WasmInstructionData) { 0, 0 })); break;
+        case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_AND16_U, ((WasmInstructionData) { 1, 0 })); break;
+
+        case Basic_Kind_I32:
+        case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_AND, ((WasmInstructionData) { 2, 0 })); break;
+
+        case Basic_Kind_I64:
+        case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_AND, ((WasmInstructionData) { 3, 0 })); break;
+
+        default: goto bad_type;
+    }
+
+    *pcode = code;
+    return;
+
+bad_type:
+    onyx_report_error(where->pos, Error_Critical, "Bad type for atomic and, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
+}
+
+EMIT_FUNC(intrinsic_atomic_or, Type* type, OnyxToken* where) {
+    if (type->kind != Type_Kind_Basic) goto bad_type;
+
+    bh_arr(WasmInstruction) code = *pcode;
+
+    switch (type->Basic.kind) {
+        case Basic_Kind_U8:  WID(NULL, WI_ATOMIC_I32_OR8_U, ((WasmInstructionData) { 0, 0 })); break;
+        case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_OR16_U, ((WasmInstructionData) { 1, 0 })); break;
+
+        case Basic_Kind_I32:
+        case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_OR, ((WasmInstructionData) { 2, 0 })); break;
+
+        case Basic_Kind_I64:
+        case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_OR, ((WasmInstructionData) { 3, 0 })); break;
+
+        default: goto bad_type;
+    }
+
+    *pcode = code;
+    return;
+
+bad_type:
+    onyx_report_error(where->pos, Error_Critical, "Bad type for atomic or, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
+}
+
+EMIT_FUNC(intrinsic_atomic_xor, Type* type, OnyxToken* where) {
+    if (type->kind != Type_Kind_Basic) goto bad_type;
+
+    bh_arr(WasmInstruction) code = *pcode;
+
+    switch (type->Basic.kind) {
+        case Basic_Kind_U8:  WID(NULL, WI_ATOMIC_I32_XOR8_U, ((WasmInstructionData) { 0, 0 })); break;
+        case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_XOR16_U, ((WasmInstructionData) { 1, 0 })); break;
+
+        case Basic_Kind_I32:
+        case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_XOR, ((WasmInstructionData) { 2, 0 })); break;
+
+        case Basic_Kind_I64:
+        case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_XOR, ((WasmInstructionData) { 3, 0 })); break;
+
+        default: goto bad_type;
+    }
+
+    *pcode = code;
+    return;
+
+bad_type:
+    onyx_report_error(where->pos, Error_Critical, "Bad type for atomic xor, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
+}
+
+EMIT_FUNC(intrinsic_atomic_xchg, Type* type, OnyxToken* where) {
+    if (type->kind != Type_Kind_Basic) goto bad_type;
+
+    bh_arr(WasmInstruction) code = *pcode;
+
+    switch (type->Basic.kind) {
+        case Basic_Kind_U8:  WID(NULL, WI_ATOMIC_I32_XCHG8_U, ((WasmInstructionData) { 0, 0 })); break;
+        case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_XCHG16_U, ((WasmInstructionData) { 1, 0 })); break;
+
+        case Basic_Kind_I32:
+        case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_XCHG, ((WasmInstructionData) { 2, 0 })); break;
+
+        case Basic_Kind_I64:
+        case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_XCHG, ((WasmInstructionData) { 3, 0 })); break;
+
+        default: goto bad_type;
+    }
+
+    *pcode = code;
+    return;
+
+bad_type:
+    onyx_report_error(where->pos, Error_Critical, "Bad type for atomic xchg, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
+}
+
+EMIT_FUNC(intrinsic_atomic_cmpxchg, Type* type, OnyxToken* where) {
+    if (type->kind != Type_Kind_Basic) goto bad_type;
+
+    bh_arr(WasmInstruction) code = *pcode;
+
+    switch (type->Basic.kind) {
+        case Basic_Kind_U8:  WID(NULL, WI_ATOMIC_I32_CMPXCHG8_U, ((WasmInstructionData) { 0, 0 })); break;
+        case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_CMPXCHG16_U, ((WasmInstructionData) { 1, 0 })); break;
+
+        case Basic_Kind_I32:
+        case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_CMPXCHG, ((WasmInstructionData) { 2, 0 })); break;
+
+        case Basic_Kind_I64:
+        case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_CMPXCHG, ((WasmInstructionData) { 3, 0 })); break;
+
+        default: goto bad_type;
+    }
+
+    *pcode = code;
+    return;
+
+bad_type:
+    onyx_report_error(where->pos, Error_Critical, "Bad type for atomic cmpxchg, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
+}
+
+EMIT_FUNC_NO_ARGS(initialize_data_segments_body) {
+    // :ProperLinking
+    if (!context.options->use_multi_threading || !context.options->use_post_mvp_features) return;
+
+    bh_arr(WasmInstruction) code = *pcode;
+
+    //
+    // Because this code is generated direction in the function
+    // it is assumed that EVERY data entry will be entered by
+    // this point. If data section entries can be entered after
+    // function body generation starts, this code will have to
+    // move to a link phase thing.
+    i32 index = 0;
+    bh_arr_each(WasmDatum, datum, mod->data) {
+        assert(datum->id > 0);
+        if (datum->data == NULL) { index++; continue; }
+
+        emit_data_relocation(mod, &code, datum->id);
+        WID(NULL, WI_PTR_CONST,   0);
+        WID(NULL, WI_I32_CONST,   datum->length);
+        WID(NULL, WI_MEMORY_INIT, ((WasmInstructionData) { index, 0 }));
+
+        index += 1;
+    }
+
+    *pcode = code;
+}
+
+EMIT_FUNC_NO_ARGS(run_init_procedures) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    bh_arr_each(AstFunction *, func, init_procedures) {
+        i32 func_idx = (i32) bh_imap_get(&mod->index_map, (u64) *func);
+        debug_emit_instruction(mod, NULL);
+        bh_arr_push(code, ((WasmInstruction){ WI_CALL, func_idx }));
+    }
+
+    *pcode = code;
+}
diff --git a/compiler/src/wasm_output.h b/compiler/src/wasm_output.h
new file mode 100644 (file)
index 0000000..74213c5
--- /dev/null
@@ -0,0 +1,1061 @@
+// This file is included in src/onyxwasm.c.
+// It is separated because of its fundamentally different goals.
+
+//-------------------------------------------------
+// BINARY OUPUT
+//-------------------------------------------------
+
+#define WASM_SECTION_ID_CUSTOM 0
+#define WASM_SECTION_ID_TYPE 1
+#define WASM_SECTION_ID_IMPORT 2
+#define WASM_SECTION_ID_FUNCTION 3
+#define WASM_SECTION_ID_TABLE 4
+#define WASM_SECTION_ID_MEMORY 5
+#define WASM_SECTION_ID_GLOBAL 6
+#define WASM_SECTION_ID_EXPORT 7
+#define WASM_SECTION_ID_START 8
+#define WASM_SECTION_ID_ELEMENT 9
+#define WASM_SECTION_ID_DATACOUNT 12
+#define WASM_SECTION_ID_CODE 10
+#define WASM_SECTION_ID_DATA 11
+
+typedef i32 vector_func(void*, bh_buffer*);
+
+static const u8 ONYX_MAGIC_STRING[] = "ONYX";
+static const u8 WASM_MAGIC_STRING[] = { 0x00, 0x61, 0x73, 0x6D };
+static const u8 WASM_VERSION[] = { 0x01, 0x00, 0x00, 0x00 };
+
+static inline i32 output_unsigned_integer(u64 i, bh_buffer *buff) {
+    i32 leb_len;
+    u8* leb = uint_to_uleb128(i, &leb_len);
+    bh_buffer_append(buff, leb, leb_len);
+    return leb_len;
+}
+
+static inline i32 output_custom_section_name(char *name, bh_buffer *buff) {
+    u32 len = strlen(name);
+    i32 len_len = output_unsigned_integer(len, buff);
+    bh_buffer_append(buff, name, len);
+    return len_len + len;
+}
+
+static void output_instruction(WasmFunc* func, WasmInstruction* instr, bh_buffer* buff);
+
+static i32 output_vector(void** arr, i32 stride, i32 arrlen, vector_func elem, bh_buffer* vec_buff) {
+    i32 len;
+    u8* leb = uint_to_uleb128((u64) arrlen, &len);
+    bh_buffer_append(vec_buff, leb, len);
+
+    i32 i = 0;
+    while (i < arrlen) {
+        elem(*arr, vec_buff);
+        arr = bh_pointer_add(arr, stride);
+        i++;
+    }
+
+    return vec_buff->length;
+}
+
+static i32 output_name(const char* start, i32 length, bh_buffer* buff) {
+    i32 leb_len, prev_len = buff->length;
+    u8* leb = uint_to_uleb128((u64) length, &leb_len);
+    bh_buffer_append(buff, leb, leb_len);
+    bh_buffer_append(buff, start, length);
+    return buff->length - prev_len;
+}
+
+static i32 output_limits(i32 min, i32 max, b32 shared, bh_buffer* buff) {
+    i32 leb_len, prev_len = buff->length;
+    u8* leb;
+
+    u8 mem_type = 0x00;
+    if (max >= 0) mem_type |= 0x01;
+    if (shared)   mem_type |= 0x02;
+
+    bh_buffer_write_byte(buff, mem_type);
+
+    leb = uint_to_uleb128((u64) min, &leb_len);
+    bh_buffer_append(buff, leb, leb_len);
+
+    if (max >= 0) {
+        leb = uint_to_uleb128((u64) max, &leb_len);
+        bh_buffer_append(buff, leb, leb_len);
+    }
+
+    return buff->length - prev_len;
+}
+
+static i32 output_functype(WasmFuncType* type, bh_buffer* buff) {
+    i32 prev_len = buff->length;
+
+    bh_buffer_write_byte(buff, 0x60);
+
+    i32 len;
+    u8* leb_buff = uint_to_uleb128(type->param_count, &len);
+    bh_buffer_append(buff, leb_buff, len);
+    bh_buffer_append(buff, type->param_types, type->param_count);
+
+    if (type->return_type != WASM_TYPE_VOID) {
+        bh_buffer_write_byte(buff, 0x01);
+        bh_buffer_write_byte(buff, type->return_type);
+    } else {
+        bh_buffer_write_byte(buff, 0x00);
+    }
+
+    return buff->length - prev_len;
+}
+
+static i32 output_typesection(OnyxWasmModule* module, bh_buffer* buff) {
+    i32 prev_len = buff->length;
+    bh_buffer_write_byte(buff, 0x01);
+
+    bh_buffer vec_buff;
+    bh_buffer_init(&vec_buff, buff->allocator, 128);
+
+    i32 vec_len = output_vector(
+                                (void**) module->types,
+                                sizeof(WasmFuncType*),
+                                bh_arr_length(module->types),
+                                (vector_func *) output_functype,
+                                &vec_buff);
+
+    i32 leb_len;
+    u8* leb = uint_to_uleb128((u64) vec_len, &leb_len);
+    bh_buffer_append(buff, leb, leb_len);
+
+    bh_buffer_concat(buff, vec_buff);
+    bh_buffer_free(&vec_buff);
+
+    return buff->length - prev_len;
+}
+
+static i32 output_funcsection(OnyxWasmModule* module, bh_buffer* buff) {
+    i32 prev_len = buff->length;
+    bh_buffer_write_byte(buff, WASM_SECTION_ID_FUNCTION);
+
+    bh_buffer vec_buff;
+    bh_buffer_init(&vec_buff, buff->allocator, 128);
+
+    i32 leb_len;
+    u8* leb = uint_to_uleb128((u64) (bh_arr_length(module->funcs)), &leb_len);
+    bh_buffer_append(&vec_buff, leb, leb_len);
+
+    bh_arr_each(WasmFunc, func, module->funcs) {
+        leb = uint_to_uleb128((u64) (func->type_idx), &leb_len);
+        bh_buffer_append(&vec_buff, leb, leb_len);
+    }
+
+    leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
+    bh_buffer_append(buff, leb, leb_len);
+
+    bh_buffer_concat(buff, vec_buff);
+    bh_buffer_free(&vec_buff);
+
+    return buff->length - prev_len;
+}
+
+static i32 output_tablesection(OnyxWasmModule* module, bh_buffer* buff) {
+    // if (bh_arr_length(module->elems) == 0) return 0;
+
+    i32 prev_len = buff->length;
+    bh_buffer_write_byte(buff, WASM_SECTION_ID_TABLE);
+
+    bh_buffer vec_buff;
+    bh_buffer_init(&vec_buff, buff->allocator, 128);
+
+    i32 leb_len;
+    u8* leb = uint_to_uleb128((u64) 1, &leb_len);
+    bh_buffer_append(&vec_buff, leb, leb_len);
+
+    // NOTE: funcrefs are the only valid table element type
+    bh_buffer_write_byte(&vec_buff, 0x70);
+    output_limits(bh_arr_length(module->elems), -1, 0, &vec_buff);
+
+    leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
+    bh_buffer_append(buff, leb, leb_len);
+
+    bh_buffer_concat(buff, vec_buff);
+    bh_buffer_free(&vec_buff);
+
+    return buff->length - prev_len;
+}
+
+static i32 output_memorysection(OnyxWasmModule* module, bh_buffer* buff) {
+    // :ProperLinking
+    // if (context.options->use_multi_threading) return 0;
+    if (!module->needs_memory_section) return 0;
+
+    i32 prev_len = buff->length;
+    bh_buffer_write_byte(buff, WASM_SECTION_ID_MEMORY);
+
+    bh_buffer vec_buff;
+    bh_buffer_init(&vec_buff, buff->allocator, 128);
+
+    i32 leb_len;
+    u8* leb = uint_to_uleb128((u64) 1, &leb_len);
+    bh_buffer_append(&vec_buff, leb, leb_len);
+
+    output_limits(module->memory_min_size, -1, 0, &vec_buff);
+
+    leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
+    bh_buffer_append(buff, leb, leb_len);
+
+    bh_buffer_concat(buff, vec_buff);
+    bh_buffer_free(&vec_buff);
+
+    return buff->length - prev_len;
+}
+
+static i32 output_globalsection(OnyxWasmModule* module, bh_buffer* buff) {
+    i32 prev_len = buff->length;
+    bh_buffer_write_byte(buff, WASM_SECTION_ID_GLOBAL);
+
+    bh_buffer vec_buff;
+    bh_buffer_init(&vec_buff, buff->allocator, 128);
+
+    i32 leb_len;
+    u8* leb = uint_to_uleb128((u64) (bh_arr_length(module->globals)), &leb_len);
+    bh_buffer_append(&vec_buff, leb, leb_len);
+
+    bh_arr_each(WasmGlobal, global, module->globals) {
+        bh_buffer_write_byte(&vec_buff, global->type);
+        bh_buffer_write_byte(&vec_buff, 0x01);
+
+        bh_arr_each(WasmInstruction, instr, global->initial_value)
+            output_instruction(NULL, instr, &vec_buff);
+
+        // NOTE: Initial value expression terminator
+        bh_buffer_write_byte(&vec_buff, (u8) WI_BLOCK_END);
+    }
+
+    leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
+    bh_buffer_append(buff, leb, leb_len);
+
+    bh_buffer_concat(buff, vec_buff);
+    bh_buffer_free(&vec_buff);
+
+    return buff->length - prev_len;
+}
+
+static i32 output_importsection(OnyxWasmModule* module, bh_buffer* buff) {
+    i32 prev_len = buff->length;
+    bh_buffer_write_byte(buff, WASM_SECTION_ID_IMPORT);
+
+    bh_buffer vec_buff;
+    bh_buffer_init(&vec_buff, buff->allocator, 128);
+
+    i32 leb_len;
+    u8* leb = uint_to_uleb128((u64) (bh_arr_length(module->imports)), &leb_len);
+    bh_buffer_append(&vec_buff, leb, leb_len);
+
+    bh_arr_each(WasmImport, import, module->imports) {
+        output_name(import->mod, strlen(import->mod), &vec_buff);
+        output_name(import->name, strlen(import->name), &vec_buff);
+        bh_buffer_write_byte(&vec_buff, (u8) import->kind);
+
+        switch (import->kind) {
+            case WASM_FOREIGN_FUNCTION:
+                leb = uint_to_uleb128((u64) import->idx, &leb_len);
+                bh_buffer_append(&vec_buff, leb, leb_len);
+                break;
+
+            case WASM_FOREIGN_MEMORY:
+                output_limits(import->min, import->max, import->shared, &vec_buff);
+                break;
+
+            case WASM_FOREIGN_TABLE: assert(0);
+        }
+    }
+
+    leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
+    bh_buffer_append(buff, leb, leb_len);
+
+    bh_buffer_concat(buff, vec_buff);
+    bh_buffer_free(&vec_buff);
+
+    return buff->length - prev_len;
+}
+
+static i32 output_exportsection(OnyxWasmModule* module, bh_buffer* buff) {
+    i32 prev_len = buff->length;
+    bh_buffer_write_byte(buff, WASM_SECTION_ID_EXPORT);
+
+    bh_buffer vec_buff;
+    bh_buffer_init(&vec_buff, buff->allocator, 128);
+
+    i32 leb_len;
+    u8* leb = uint_to_uleb128((u64) (module->export_count), &leb_len);
+    bh_buffer_append(&vec_buff, leb, leb_len);
+
+    i32 key_len = 0;
+    fori (i, 0, shlen(module->exports)) {
+        char *key = module->exports[i].key;
+        WasmExport value = module->exports[i].value;
+
+        key_len = strlen(key);
+        output_name(key, key_len, &vec_buff);
+
+        bh_buffer_write_byte(&vec_buff, (u8) (value.kind));
+        leb = uint_to_uleb128((u64) value.idx, &leb_len);
+        bh_buffer_append(&vec_buff, leb, leb_len);
+    }
+
+    leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
+    bh_buffer_append(buff, leb, leb_len);
+
+    bh_buffer_concat(buff, vec_buff);
+    bh_buffer_free(&vec_buff);
+
+    return buff->length - prev_len;
+}
+
+static i32 output_startsection(OnyxWasmModule* module, bh_buffer* buff) {
+    i32 prev_len = buff->length;
+
+    i32 start_idx = -1;
+    fori (i, 0, shlen(module->exports)) {
+        char *key = module->exports[i].key;
+        WasmExport value = module->exports[i].value;
+
+        if (value.kind == WASM_FOREIGN_FUNCTION) {
+            if (strncmp("main", key, 5) == 0) {
+                start_idx = value.idx;
+                break;
+            }
+        }
+    }
+
+    if (start_idx != -1) {
+        bh_buffer_write_byte(buff, WASM_SECTION_ID_START);
+
+        i32 start_leb_len, section_leb_len;
+        uint_to_uleb128((u64) start_idx, &start_leb_len);
+        u8* section_leb = uint_to_uleb128((u64) start_leb_len, &section_leb_len);
+        bh_buffer_append(buff, section_leb, section_leb_len);
+
+        u8* start_leb = uint_to_uleb128((u64) start_idx, &start_leb_len);
+        bh_buffer_append(buff, start_leb, start_leb_len);
+    }
+
+    return buff->length - prev_len;
+}
+
+static i32 output_elemsection(OnyxWasmModule* module, bh_buffer* buff) {
+    if (bh_arr_length(module->elems) == 0) return 0;
+
+    i32 prev_len = buff->length;
+
+    bh_buffer_write_byte(buff, WASM_SECTION_ID_ELEMENT);
+
+    bh_buffer vec_buff;
+    bh_buffer_init(&vec_buff, buff->allocator, 128);
+
+    i32 leb_len;
+    u8* leb;
+
+    // NOTE: 0x01 count of elems
+    bh_buffer_write_byte(&vec_buff, 0x01);
+
+    // NOTE: 0x00 table index
+    bh_buffer_write_byte(&vec_buff, 0x00);
+
+    bh_buffer_write_byte(&vec_buff, WI_I32_CONST);
+    bh_buffer_write_byte(&vec_buff, 0x00);
+    bh_buffer_write_byte(&vec_buff, WI_BLOCK_END);
+
+    leb = uint_to_uleb128((u64) bh_arr_length(module->elems), &leb_len);
+    bh_buffer_append(&vec_buff, leb, leb_len);
+
+    bh_arr_each(i32, elem, module->elems) {
+        leb = uint_to_uleb128((u64) *elem, &leb_len);
+        bh_buffer_append(&vec_buff, leb, leb_len);
+    }
+
+    leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
+    bh_buffer_append(buff, leb, leb_len);
+
+    bh_buffer_concat(buff, vec_buff);
+    bh_buffer_free(&vec_buff);
+
+    return buff->length - prev_len;
+}
+
+static i32 output_locals(WasmFunc* func, bh_buffer* buff) {
+    i32 prev_len = buff->length;
+
+    // NOTE: Output vector length
+    i32 total_locals =
+        (i32) (func->locals.allocated[0] != 0) +
+        (i32) (func->locals.allocated[1] != 0) +
+        (i32) (func->locals.allocated[2] != 0) +
+        (i32) (func->locals.allocated[3] != 0) +
+        (i32) (func->locals.allocated[4] != 0);
+
+    i32 leb_len;
+    u8* leb = uint_to_uleb128((u64) total_locals, &leb_len);
+    bh_buffer_append(buff, leb, leb_len);
+
+    if (func->locals.allocated[0] != 0) {
+        leb = uint_to_uleb128((u64) func->locals.allocated[0], &leb_len);
+        bh_buffer_append(buff, leb, leb_len);
+        bh_buffer_write_byte(buff, WASM_TYPE_INT32);
+    }
+    if (func->locals.allocated[1] != 0) {
+        leb = uint_to_uleb128((u64) func->locals.allocated[1], &leb_len);
+        bh_buffer_append(buff, leb, leb_len);
+        bh_buffer_write_byte(buff, WASM_TYPE_INT64);
+    }
+    if (func->locals.allocated[2] != 0) {
+        leb = uint_to_uleb128((u64) func->locals.allocated[2], &leb_len);
+        bh_buffer_append(buff, leb, leb_len);
+        bh_buffer_write_byte(buff, WASM_TYPE_FLOAT32);
+    }
+    if (func->locals.allocated[3] != 0) {
+        leb = uint_to_uleb128((u64) func->locals.allocated[3], &leb_len);
+        bh_buffer_append(buff, leb, leb_len);
+        bh_buffer_write_byte(buff, WASM_TYPE_FLOAT64);
+    }
+    if (func->locals.allocated[4] != 0) {
+        leb = uint_to_uleb128((u64) func->locals.allocated[4], &leb_len);
+        bh_buffer_append(buff, leb, leb_len);
+        bh_buffer_write_byte(buff, WASM_TYPE_VAR128);
+    }
+
+    return buff->length - prev_len;
+}
+
+static void output_instruction(WasmFunc* func, WasmInstruction* instr, bh_buffer* buff) {
+    i32 leb_len;
+    u8* leb;
+
+    if (instr->type == WI_UNREACHABLE) assert(("EMITTING UNREACHABLE!!", 0));
+
+    if (instr->type == WI_NOP && !context.options->debug_enabled) return;
+
+    if (instr->type & SIMD_INSTR_MASK) {
+        bh_buffer_write_byte(buff, 0xFD);
+        leb = uint_to_uleb128((u64) (instr->type &~ SIMD_INSTR_MASK), &leb_len);
+        bh_buffer_append(buff, leb, leb_len);
+
+    } else if (instr->type & EXT_INSTR_MASK) {
+        bh_buffer_write_byte(buff, 0xFC);
+        leb = uint_to_uleb128((u64) (instr->type &~ EXT_INSTR_MASK), &leb_len);
+        bh_buffer_append(buff, leb, leb_len);
+
+    } else if (instr->type & ATOMIC_INSTR_MASK) {
+        bh_buffer_write_byte(buff, 0xFE);
+        leb = uint_to_uleb128((u64) (instr->type &~ ATOMIC_INSTR_MASK), &leb_len);
+        bh_buffer_append(buff, leb, leb_len);
+
+        if (instr->type == WI_ATOMIC_FENCE) {
+            bh_buffer_write_byte(buff, 0x00);
+
+        } else {
+            leb = uint_to_uleb128((u64) instr->data.i1, &leb_len);
+            bh_buffer_append(buff, leb, leb_len);
+
+            leb = uint_to_uleb128((u64) instr->data.i2, &leb_len);
+            bh_buffer_append(buff, leb, leb_len);
+        }
+
+    } else {
+        bh_buffer_write_byte(buff, (u8) instr->type);
+    }
+
+    switch (instr->type) {
+        case WI_LOCAL_GET:
+        case WI_LOCAL_SET:
+        case WI_LOCAL_TEE: {
+            u64 actual_idx = local_lookup_idx(&func->locals, instr->data.l);
+            leb = uint_to_uleb128(actual_idx, &leb_len);
+            bh_buffer_append(buff, leb, leb_len);
+            break;
+        }
+
+        case WI_GLOBAL_GET:
+        case WI_GLOBAL_SET:
+        case WI_CALL:
+        case WI_BLOCK_START:
+        case WI_LOOP_START:
+        case WI_JUMP:
+        case WI_COND_JUMP:
+        case WI_IF_START:
+        case WI_MEMORY_SIZE:
+        case WI_MEMORY_GROW:
+        case WI_MEMORY_FILL:
+            leb = uint_to_uleb128((u64) instr->data.i1, &leb_len);
+            bh_buffer_append(buff, leb, leb_len);
+            break;
+
+        case WI_MEMORY_INIT:
+        case WI_MEMORY_COPY:
+            leb = uint_to_uleb128((u64) instr->data.i1, &leb_len);
+            bh_buffer_append(buff, leb, leb_len);
+
+            leb = uint_to_uleb128((u64) instr->data.i2, &leb_len);
+            bh_buffer_append(buff, leb, leb_len);
+            break;
+
+        case WI_JUMP_TABLE: {
+            BranchTable* bt = (BranchTable *) instr->data.p;
+
+            leb = uint_to_uleb128((u64) bt->count, &leb_len);
+            bh_buffer_append(buff, leb, leb_len);
+
+            fori (i, 0, bt->count) {
+                leb = uint_to_uleb128((u64) bt->cases[i], &leb_len);
+                bh_buffer_append(buff, leb, leb_len);
+            }
+
+            leb = uint_to_uleb128((u64) bt->default_case, &leb_len);
+            bh_buffer_append(buff, leb, leb_len);
+            break;
+        }
+
+
+        case WI_CALL_INDIRECT:
+        case WI_I32_STORE: case WI_I32_STORE_8: case WI_I32_STORE_16:
+        case WI_I64_STORE: case WI_I64_STORE_8: case WI_I64_STORE_16: case WI_I64_STORE_32:
+        case WI_F32_STORE: case WI_F64_STORE:
+        case WI_V128_STORE:
+        case WI_I32_LOAD:
+        case WI_I32_LOAD_8_S: case WI_I32_LOAD_8_U:
+        case WI_I32_LOAD_16_S: case WI_I32_LOAD_16_U:
+        case WI_I64_LOAD:
+        case WI_I64_LOAD_8_S: case WI_I64_LOAD_8_U:
+        case WI_I64_LOAD_16_S: case WI_I64_LOAD_16_U:
+        case WI_I64_LOAD_32_S: case WI_I64_LOAD_32_U:
+        case WI_F32_LOAD: case WI_F64_LOAD:
+        case WI_V128_LOAD:
+            leb = uint_to_uleb128((u64) instr->data.i1, &leb_len);
+            bh_buffer_append(buff, leb, leb_len);
+            leb = uint_to_uleb128((u64) instr->data.i2, &leb_len);
+            bh_buffer_append(buff, leb, leb_len);
+            break;
+
+        case WI_I32_CONST:
+            leb = int_to_leb128((i64) instr->data.i1, &leb_len);
+            bh_buffer_append(buff, leb, leb_len);
+            break;
+        case WI_I64_CONST:
+            leb = int_to_leb128((i64) instr->data.l, &leb_len);
+            bh_buffer_append(buff, leb, leb_len);
+            break;
+        case WI_F32_CONST:
+            leb = float_to_ieee754(instr->data.f, 0);
+            bh_buffer_append(buff, leb, 4);
+            break;
+        case WI_F64_CONST:
+            leb = double_to_ieee754(instr->data.d, 0);
+            bh_buffer_append(buff, leb, 8);
+            break;
+
+        case WI_V128_CONST:
+        case WI_I8X16_SHUFFLE:
+            fori (i, 0, 16) bh_buffer_write_byte(buff, ((u8*) instr->data.p)[i]);
+            break;
+
+        case WI_I8X16_EXTRACT_LANE_S: case WI_I8X16_EXTRACT_LANE_U: case WI_I8X16_REPLACE_LANE:
+        case WI_I16X8_EXTRACT_LANE_S: case WI_I16X8_EXTRACT_LANE_U: case WI_I16X8_REPLACE_LANE:
+        case WI_I32X4_EXTRACT_LANE: case WI_I32X4_REPLACE_LANE:
+        case WI_I64X2_EXTRACT_LANE: case WI_I64X2_REPLACE_LANE:
+        case WI_F32X4_EXTRACT_LANE: case WI_F32X4_REPLACE_LANE:
+        case WI_F64X2_EXTRACT_LANE: case WI_F64X2_REPLACE_LANE:
+            bh_buffer_write_byte(buff, (u8) instr->data.i1);
+            break;
+
+        default: break;
+    }
+}
+
+static i32 output_code(WasmFunc* func, bh_buffer* buff) {
+
+    bh_buffer code_buff;
+    bh_buffer_init(&code_buff, buff->allocator, 128);
+
+    // Output locals
+    output_locals(func, &code_buff);
+
+    assert(func->code);
+
+    // Output code
+    bh_arr_each(WasmInstruction, instr, func->code) output_instruction(func, instr, &code_buff);
+
+    i32 leb_len;
+    u8* leb = uint_to_uleb128((u64) code_buff.length, &leb_len);
+    bh_buffer_append(buff, leb, leb_len);
+
+    bh_buffer_concat(buff, code_buff);
+    bh_buffer_free(&code_buff);
+
+    return 0;
+}
+
+static i32 output_codesection(OnyxWasmModule* module, bh_buffer* buff) {
+    i32 prev_len = buff->length;
+
+    bh_buffer_write_byte(buff, WASM_SECTION_ID_CODE);
+
+    bh_buffer vec_buff;
+    bh_buffer_init(&vec_buff, buff->allocator, 128);
+
+    i32 leb_len;
+    u8* leb = uint_to_uleb128((u64) bh_arr_length(module->funcs), &leb_len);
+    bh_buffer_append(&vec_buff, leb, leb_len);
+
+    bh_arr_each(WasmFunc, func, module->funcs) output_code(func, &vec_buff);
+
+    leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
+    bh_buffer_append(buff, leb, leb_len);
+
+    bh_buffer_concat(buff, vec_buff);
+    bh_buffer_free(&vec_buff);
+
+    return buff->length - prev_len;
+}
+
+static i32 output_datacountsection(OnyxWasmModule* module, bh_buffer* buff) {
+    if (!context.options->use_post_mvp_features) return 0;
+
+    i32 prev_len = buff->length;
+
+    bh_buffer_write_byte(buff, WASM_SECTION_ID_DATACOUNT);
+
+    bh_buffer vec_buff;
+    bh_buffer_init(&vec_buff, buff->allocator, 128);
+
+    i32 leb_len;
+    u8* leb = uint_to_uleb128((u64) bh_arr_length(module->data), &leb_len);
+    bh_buffer_append(&vec_buff, leb, leb_len);
+
+    leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
+    bh_buffer_append(buff, leb, leb_len);
+
+    bh_buffer_concat(buff, vec_buff);
+    bh_buffer_free(&vec_buff);
+
+    return buff->length - prev_len;
+}
+
+static i32 output_datasection(OnyxWasmModule* module, bh_buffer* buff) {
+    i32 prev_len = buff->length;
+
+    bh_buffer_write_byte(buff, WASM_SECTION_ID_DATA);
+
+    bh_buffer vec_buff;
+    bh_buffer_init(&vec_buff, buff->allocator, 128);
+
+    i32 leb_len;
+    u8* leb = uint_to_uleb128((u64) bh_arr_length(module->data), &leb_len);
+    bh_buffer_append(&vec_buff, leb, leb_len);
+
+    bh_arr_each(WasmDatum, datum, module->data) {
+        i32 memory_flags = 0x00;
+        // :ProperLinking
+        if (context.options->use_multi_threading) memory_flags |= 0x01;
+
+        bh_buffer_write_byte(&vec_buff, memory_flags);
+
+        // :ProperLinking
+        if (!context.options->use_multi_threading) {
+            bh_buffer_write_byte(&vec_buff, WI_I32_CONST);
+            leb = int_to_leb128((i64) datum->offset_, &leb_len);
+            bh_buffer_append(&vec_buff, leb, leb_len);
+            bh_buffer_write_byte(&vec_buff, WI_BLOCK_END);
+        }
+
+        if (datum->data != NULL) {
+            leb = uint_to_uleb128((u64) datum->length, &leb_len);
+            bh_buffer_append(&vec_buff, leb, leb_len);
+            fori (i, 0, datum->length) bh_buffer_write_byte(&vec_buff, ((u8 *) datum->data)[i]);
+        } else {
+            leb = uint_to_uleb128(0, &leb_len);
+            bh_buffer_append(&vec_buff, leb, leb_len);
+        }
+    }
+
+    leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
+    bh_buffer_append(buff, leb, leb_len);
+
+    bh_buffer_concat(buff, vec_buff);
+    bh_buffer_free(&vec_buff);
+
+    return buff->length - prev_len;
+}
+
+static i32 output_onyx_libraries_section(OnyxWasmModule* module, bh_buffer* buff) {
+    if (bh_arr_length(module->libraries) == 0) return 0;
+    i32 prev_len = buff->length;
+
+    bh_buffer_write_byte(buff, WASM_SECTION_ID_CUSTOM);
+
+    bh_buffer libs_buff;
+    bh_buffer_init(&libs_buff, buff->allocator, 128);
+
+    output_custom_section_name("_onyx_libs", &libs_buff);
+
+    output_unsigned_integer(bh_arr_length(module->library_paths), &libs_buff);
+
+    bh_arr_each(char *, lib, module->library_paths) {
+        assert(*lib != NULL);
+
+        u32 lib_len = strlen(*lib);
+        output_unsigned_integer(lib_len, &libs_buff);
+        bh_buffer_append(&libs_buff, *lib, lib_len);
+    }
+
+    output_unsigned_integer(bh_arr_length(module->libraries), &libs_buff);
+
+    bh_arr_each(char *, lib, module->libraries) {
+        assert(*lib != NULL);
+
+        u32 lib_len = strlen(*lib);
+        output_unsigned_integer(lib_len, &libs_buff);
+        bh_buffer_append(&libs_buff, *lib, lib_len);
+    }
+
+    output_unsigned_integer(libs_buff.length, buff);
+
+    bh_buffer_concat(buff, libs_buff);
+    bh_buffer_free(&libs_buff);
+
+    return buff->length - prev_len;
+}
+
+static i32 output_onyx_func_offset_section(OnyxWasmModule* module, bh_buffer* buff) {
+    i32 prev_len = buff->length;
+
+    bh_buffer_write_byte(buff, WASM_SECTION_ID_CUSTOM);
+
+    bh_buffer section_buff;
+    bh_buffer_init(&section_buff, buff->allocator, 128);
+
+    output_custom_section_name("_onyx_func_offsets", &section_buff);
+
+    i32 func_count = bh_arr_length(module->funcs) + module->foreign_function_count;
+
+    bh_buffer name_buff;
+    bh_buffer_init(&name_buff, buff->allocator, 1024);
+    u32 str_cursor = func_count * 4;
+    fori (i, 0, func_count) {
+        bh_buffer_write_u32(&section_buff, str_cursor);
+
+        if (i < module->foreign_function_count) {
+            bh_buffer_append(&name_buff, "<imported function>", 20);
+            str_cursor += 20;
+        } else {
+            WasmFunc *func = &module->funcs[i - module->foreign_function_count];
+            assert(func->location);
+            char *str = bh_bprintf("%s:%d,%d\0", func->location->pos.filename, func->location->pos.line, func->location->pos.column);
+            i32 len = strlen(str);
+            bh_buffer_append(&name_buff, str, len + 1);
+            str_cursor += len + 1;
+        }
+    }
+
+    bh_buffer_concat(&section_buff, name_buff);
+
+    output_unsigned_integer(section_buff.length, buff);
+
+    bh_buffer_concat(buff, section_buff);
+    bh_buffer_free(&section_buff);
+
+    return buff->length - prev_len;
+}
+
+#ifdef ENABLE_DEBUG_INFO
+static i32 output_ovm_debug_sections(OnyxWasmModule* module, bh_buffer* buff) {
+    if (!module->debug_context || !context.options->debug_enabled) return 0;
+
+    DebugContext *ctx = module->debug_context;
+
+    bh_buffer section_buff;
+    bh_buffer_init(&section_buff, buff->allocator, 128);
+
+    {
+        // ovm_debug_files section
+        bh_buffer_clear(&section_buff);
+        bh_buffer_write_byte(buff, WASM_SECTION_ID_CUSTOM);
+
+        output_custom_section_name("ovm_debug_files", &section_buff);
+
+        i32 file_count = shlenu(ctx->file_info);
+        output_unsigned_integer(file_count, &section_buff);
+
+        fori (i, 0, file_count) {
+            Table(DebugFileInfo) entry = (void *) &ctx->file_info[i];
+            output_unsigned_integer(entry->value.file_id, &section_buff);
+            output_unsigned_integer(entry->value.line_count, &section_buff);
+            output_name(entry->key, strlen(entry->key), &section_buff);
+        }
+
+        output_unsigned_integer(section_buff.length, buff);
+
+        bh_buffer_concat(buff, section_buff);
+    }
+
+    {
+        // ovm_debug_funcs section
+        bh_buffer_clear(&section_buff);
+        bh_buffer_write_byte(buff, WASM_SECTION_ID_CUSTOM);
+
+        output_custom_section_name("ovm_debug_funcs", &section_buff);
+
+        i32 func_count = bh_arr_length(ctx->funcs);
+        output_unsigned_integer(func_count, &section_buff);
+
+        fori (i, 0, func_count) {
+            DebugFuncContext *func = &ctx->funcs[i];
+            output_unsigned_integer(func->func_index, &section_buff);
+            output_unsigned_integer(func->file_id, &section_buff);
+            output_unsigned_integer(func->line, &section_buff);
+            output_name(func->name, func->name_length, &section_buff);
+            output_unsigned_integer(1, &section_buff);
+            output_unsigned_integer(func->op_offset, &section_buff);
+
+            LocalAllocator *locals = &module->funcs[i].locals;
+            if (func->stack_ptr_idx > 0) {
+                u32 local_idx = local_lookup_idx(locals, func->stack_ptr_idx);
+                output_unsigned_integer(local_idx, &section_buff);
+            } else {
+                output_unsigned_integer(0, &section_buff);
+            }
+
+            output_unsigned_integer(0, &section_buff);
+        }
+
+        output_unsigned_integer(section_buff.length, buff);
+
+        bh_buffer_concat(buff, section_buff);
+    }
+
+    {
+        // ovm_debug_syms section
+
+        // First, apply patches for register locations
+        bh_arr_each(DebugSymPatch, patch, ctx->sym_patches) {
+            // CLEANUP: This is (kind of) incorrect, as there is nothing guarenteeing
+            // that the symbol with id a will be a position a, other than the way
+            // that this has been implemented right now.
+            assert(ctx->sym_info[patch->sym_id].location_type == DSL_REGISTER);
+
+            LocalAllocator *locals = &module->funcs[patch->func_idx - module->foreign_function_count].locals;
+            ctx->sym_info[patch->sym_id].location_num = local_lookup_idx(locals, patch->local_idx);
+        }
+
+        bh_buffer_clear(&section_buff);
+        bh_buffer_write_byte(buff, WASM_SECTION_ID_CUSTOM);
+
+        output_custom_section_name("ovm_debug_syms", &section_buff);
+
+        i32 sym_count = bh_arr_length(ctx->sym_info);
+        output_unsigned_integer(sym_count, &section_buff);
+
+        fori (i, 0, sym_count) {
+            DebugSymInfo *sym = &ctx->sym_info[i];
+            output_unsigned_integer(sym->sym_id, &section_buff);
+            if (sym->name) {
+                output_name(sym->name, strlen(sym->name), &section_buff);
+            } else {
+                output_unsigned_integer(0, &section_buff);
+            }
+            output_unsigned_integer(sym->location_type, &section_buff);
+            output_unsigned_integer(sym->location_num, &section_buff);
+            output_unsigned_integer(sym->type, &section_buff);
+        }
+
+        output_unsigned_integer(section_buff.length, buff);
+
+        bh_buffer_concat(buff, section_buff);
+    }
+
+    {
+        // ovm_debug_types section
+        bh_buffer_clear(&section_buff);
+        bh_buffer_write_byte(buff, WASM_SECTION_ID_CUSTOM);
+
+        output_custom_section_name("ovm_debug_types", &section_buff);
+
+        i32 type_count = bh_arr_length(type_map.entries);
+        output_unsigned_integer(type_count, &section_buff);
+
+        bh_arr_each(bh__imap_entry, entry, type_map.entries) {
+            u32 id     = entry->key;
+            Type *type = (Type *) entry->value;
+            const char *name = type_get_name(type);
+
+            output_unsigned_integer(id, &section_buff);
+            output_name(name, strlen(name), &section_buff);
+            output_unsigned_integer(type_size_of(type), &section_buff);
+
+            if (type->kind == Type_Kind_Basic) {
+                // Type indicies are special because they are encoded
+                // as a "distinct" unsigned 32-bit integer, which is
+                // effectively how they are used in the code anyway.
+                //
+                // This is probably a change that will be made throughout
+                // the entire compiler, but for now they will remain as
+                // a special type.
+                if (type->Basic.kind == Basic_Kind_Type_Index) {
+                    output_unsigned_integer(5, &section_buff);
+                    output_unsigned_integer(2, &section_buff);
+                    output_unsigned_integer(basic_types[Basic_Kind_U32].id, &section_buff);
+                    continue;
+                }
+
+                if (type->Basic.kind == Basic_Kind_Rawptr) {
+                    // rawptr -> ^void
+                    output_unsigned_integer(2, &section_buff);
+                    output_unsigned_integer(1, &section_buff);
+                    output_unsigned_integer(basic_types[Basic_Kind_Void].id, &section_buff);
+                    continue;
+                }
+
+                output_unsigned_integer(1, &section_buff);
+                if      (type->Basic.kind == Basic_Kind_Void) output_unsigned_integer(0, &section_buff);
+                else if (type_is_bool(type))                  output_unsigned_integer(4, &section_buff);
+                else if (type_is_integer(type)) {
+                    if (type->Basic.flags & Basic_Flag_Unsigned) output_unsigned_integer(2, &section_buff);
+                    else                                         output_unsigned_integer(1, &section_buff);
+                }
+                else if (type->Basic.flags & Basic_Flag_Float)   output_unsigned_integer(3, &section_buff);
+                else if (type_is_simd(type))                     output_unsigned_integer(6, &section_buff);
+                else {
+                    output_unsigned_integer(0, &section_buff);
+                }
+
+                continue;
+            }
+
+            if (type->kind == Type_Kind_Pointer) {
+                output_unsigned_integer(2, &section_buff);
+                output_unsigned_integer(1, &section_buff);
+                output_unsigned_integer(type->Pointer.elem->id, &section_buff);
+                continue; 
+            }
+
+            if (type->kind == Type_Kind_Enum) {
+                output_unsigned_integer(5, &section_buff);
+                output_unsigned_integer(2, &section_buff);
+                output_unsigned_integer(type->Enum.backing->id, &section_buff);
+                continue;
+            }
+
+            if (type->kind == Type_Kind_Array) {
+                output_unsigned_integer(4, &section_buff);
+                output_unsigned_integer(type->Array.count, &section_buff);
+                output_unsigned_integer(type->Array.elem->id, &section_buff);
+                continue;
+            }
+
+            if (type_is_structlike_strict(type)) {
+                output_unsigned_integer(3, &section_buff);
+
+                i32 mem_count = type_structlike_mem_count(type);
+                output_unsigned_integer(mem_count, &section_buff);
+
+                fori (i, 0, mem_count) {
+                    StructMember smem;
+                    type_lookup_member_by_idx(type, i, &smem);
+
+                    output_unsigned_integer(smem.offset, &section_buff);
+                    output_unsigned_integer(smem.type->id, &section_buff);
+                    output_name(smem.name, strlen(smem.name), &section_buff);
+                }
+
+                continue;
+            }
+
+            if (type->kind == Type_Kind_Function) {
+                output_unsigned_integer(6, &section_buff);
+                output_unsigned_integer(type->Function.param_count, &section_buff);
+
+                fori (i, 0, (i32) type->Function.param_count) {
+                    output_unsigned_integer(type->Function.params[i]->id, &section_buff);
+                }
+
+                output_unsigned_integer(type->Function.return_type->id, &section_buff);
+                continue;
+            }
+
+            if (type->kind == Type_Kind_Distinct) {
+                output_unsigned_integer(5, &section_buff);
+                output_unsigned_integer(2, &section_buff);
+                output_unsigned_integer(type->Distinct.base_type->id, &section_buff);
+                continue;
+            }
+
+            // No debug information will be given about the poly struct
+            // or compound types.
+            // Outside of runtime type information, they provide no useful
+            // debugging information (I don't think at least...).
+            if (type->kind == Type_Kind_PolyStruct ||
+                type->kind == Type_Kind_Compound) {
+                output_unsigned_integer(1, &section_buff);
+                output_unsigned_integer(0, &section_buff);
+                continue;
+            }
+
+            assert(("Unhandled type", 0));
+        }
+
+        output_unsigned_integer(section_buff.length, buff);
+
+        bh_buffer_concat(buff, section_buff);
+    }
+
+    {
+        // ovm_debug_ops section
+        bh_buffer_clear(&section_buff);
+        bh_buffer_write_byte(buff, WASM_SECTION_ID_CUSTOM);
+
+        output_custom_section_name("ovm_debug_ops", &section_buff);
+        bh_buffer_concat(&section_buff, ctx->op_buffer);
+
+        output_unsigned_integer(section_buff.length, buff);
+
+        bh_buffer_concat(buff, section_buff);
+    }
+
+    bh_buffer_free(&section_buff);
+    return 0;
+}
+#endif
+
+void onyx_wasm_module_write_to_buffer(OnyxWasmModule* module, bh_buffer* buffer) {
+    bh_buffer_init(buffer, global_heap_allocator, 128);
+    if (context.options->runtime == Runtime_Onyx) {
+        bh_buffer_append(buffer, ONYX_MAGIC_STRING, 4);
+    } else {
+        bh_buffer_append(buffer, WASM_MAGIC_STRING, 4);
+    }
+    bh_buffer_append(buffer, WASM_VERSION, 4);
+
+#ifdef ENABLE_DEBUG_INFO
+    output_ovm_debug_sections(module, buffer);
+#endif
+    output_typesection(module, buffer);
+    output_importsection(module, buffer);
+    output_funcsection(module, buffer);
+    output_tablesection(module, buffer);
+    output_memorysection(module, buffer);
+    output_globalsection(module, buffer);
+    output_exportsection(module, buffer);
+    output_startsection(module, buffer);
+    output_elemsection(module, buffer);
+    output_datacountsection(module, buffer);
+    output_codesection(module, buffer);
+    output_datasection(module, buffer);
+    output_onyx_libraries_section(module, buffer);
+
+    // TODO: Consider if this should always be included?
+    // It can amount to a lot of extra data.
+    output_onyx_func_offset_section(module, buffer);
+}
+
+void onyx_wasm_module_write_to_file(OnyxWasmModule* module, bh_file file) {
+    bh_buffer master_buffer;
+    onyx_wasm_module_write_to_buffer(module, &master_buffer);
+
+    bh_file_write(&file, master_buffer.data, master_buffer.length);
+}
diff --git a/compiler/src/wasm_runtime.c b/compiler/src/wasm_runtime.c
new file mode 100644 (file)
index 0000000..09797d4
--- /dev/null
@@ -0,0 +1,434 @@
+#include "bh.h"
+#include "utils.h"
+#include "astnodes.h"
+#include "wasm.h"
+#include "onyx_library.h"
+
+#ifndef USE_OVM_DEBUGGER
+    #include "wasmer.h"
+#endif
+
+#ifdef _BH_LINUX
+    #include <pthread.h>
+    #include <signal.h>
+    #include <sys/wait.h>
+    #include <dlfcn.h>
+#endif
+
+static wasm_config_t*    wasm_config;
+static wasm_engine_t*    wasm_engine;
+static wasm_store_t*     wasm_store;
+static wasm_extern_vec_t wasm_imports;
+static bh_buffer         wasm_raw_bytes;
+wasm_instance_t*  wasm_instance;
+wasm_module_t*    wasm_module;
+wasm_memory_t*    wasm_memory;
+
+OnyxRuntime wasm_runtime;
+
+b32 wasm_name_equals(const wasm_name_t* name1, const wasm_name_t* name2) {
+    if (name1->size != name2->size) return 0;
+    return !strncmp(name1->data, name2->data, name1->size);
+}
+
+b32 wasm_name_equals_string(const wasm_name_t* name1, const char* name2) {
+    u32 name2_size = strlen(name2);
+    if (name1->size != name2_size) return 0;
+    return !strncmp(name1->data, name2, name1->size);
+}
+
+wasm_extern_t* wasm_extern_lookup_by_name(wasm_module_t* module, wasm_instance_t* instance, const char* name) {
+    i32 name_len = strlen(name);
+
+    i32 idx = -1;
+    wasm_exporttype_vec_t export_types;
+    wasm_module_exports(module, &export_types);
+    fori (i, 0, (i64) export_types.size) {
+        wasm_exporttype_t* export_type = export_types.data[i];
+        const wasm_name_t* export_name = wasm_exporttype_name(export_type);
+
+        if (!strncmp(export_name->data, name, name_len)) {
+            idx = i;
+            break;
+        }
+    }
+
+    if (idx == -1) return NULL;
+
+    wasm_extern_vec_t exports;
+    wasm_instance_exports(instance, &exports);
+
+    return exports.data[idx];
+}
+
+
+
+typedef void *(*LibraryLinker)(OnyxRuntime *runtime);
+
+typedef struct LibraryLinkContext {
+    bh_buffer wasm_bytes;
+    bh_arr(char *) library_paths;
+} LibraryLinkContext;
+
+static WasmFuncDefinition** onyx_load_library(LibraryLinkContext *ctx, char *name) {
+    #ifdef _BH_LINUX
+        #define DIR_SEPARATOR '/'
+    #endif
+    #ifdef _BH_WINDOWS
+        #define DIR_SEPARATOR '\\'
+    #endif
+
+    char *library = name;
+    fori (i, 0, (i32) strlen(name)) {
+        if (name[i] == DIR_SEPARATOR) library = &name[i + 1];
+    }
+
+    char *library_load_name_tmp = bh_bprintf("onyx_library_%s", library);
+    char *library_load_name = alloca(strlen(library_load_name_tmp) + 1);
+    strcpy(library_load_name, library_load_name_tmp);
+    LibraryLinker library_load;
+
+    #ifdef _BH_LINUX
+    char *library_name = bh_lookup_file(name, ".", ".so", 1, (const char **) ctx->library_paths, 1);
+    void* handle = dlopen(library_name, RTLD_LAZY);
+    if (handle == NULL) {
+        printf("ERROR LOADING LIBRARY %s: %s\n", name, dlerror());
+        return NULL;
+    }
+
+    library_load = (LibraryLinker) dlsym(handle, library_load_name);
+    if (library_load == NULL) {
+        printf("ERROR RESOLVING '%s': %s\n", library_load_name, dlerror());
+        return NULL;
+    }
+    #endif
+
+    #ifdef _BH_WINDOWS
+    char *library_name = bh_lookup_file(name, ".", ".dll", 1, (const char **) ctx->library_paths, 1);
+    HMODULE handle = LoadLibraryA(library_name);
+    if (handle == NULL) {
+        printf("ERROR LOADING LIBRARY %s: %d\n", name, GetLastError());
+        return NULL;
+    }
+
+    library_load = (LibraryLinker) GetProcAddress(handle, library_load_name);
+    if (library_load == NULL) {
+        printf("ERROR RESOLVING '%s': %d\n", library_load_name, GetLastError());
+        return NULL;
+    }
+    #endif
+
+    return library_load(runtime);
+}
+
+static void lookup_and_load_custom_libraries(LibraryLinkContext *ctx, bh_arr(WasmFuncDefinition **)* p_out) {
+    bh_arr(WasmFuncDefinition **) out = *p_out;
+
+    bh_buffer wasm_bytes = ctx->wasm_bytes;
+
+    i32 cursor = 8; // skip the magic number and version
+    while (cursor < wasm_bytes.length) {
+        u64 section_number = uleb128_to_uint(wasm_bytes.data, &cursor);
+        u64 section_size   = uleb128_to_uint(wasm_bytes.data, &cursor);
+
+        i32 section_start = cursor;
+        if (section_number == 0) {
+            u64 name_len = uleb128_to_uint(wasm_bytes.data, &cursor);
+            if (!strncmp(wasm_bytes.data + cursor, "_onyx_libs", name_len)) {
+                cursor += name_len;
+                u64 lib_count = uleb128_to_uint(wasm_bytes.data, &cursor);
+
+                fori (i, 0, (i64) lib_count) {
+                    u64 lib_path_length = uleb128_to_uint(wasm_bytes.data, &cursor);
+                    lib_path_length = bh_min(lib_path_length, 512);
+
+                    char *lib_path = malloc(lib_path_length);
+                    strncpy(lib_path, wasm_bytes.data + cursor, lib_path_length);
+                    lib_path[lib_path_length] = '\0';
+                    bh_path_convert_separators(lib_path);
+                    cursor += lib_path_length;
+
+                    bh_arr_push(ctx->library_paths, lib_path);
+                }
+
+                lib_count = uleb128_to_uint(wasm_bytes.data, &cursor);
+
+                fori (i, 0, (i64) lib_count) {
+                    u64 lib_name_length = uleb128_to_uint(wasm_bytes.data, &cursor);
+                    lib_name_length = bh_min(lib_name_length, 256);
+
+                    char library_name[256];
+                    strncpy(library_name, wasm_bytes.data + cursor, lib_name_length);
+                    library_name[lib_name_length] = '\0';
+                    cursor += lib_name_length;
+
+                    WasmFuncDefinition** lib = onyx_load_library(ctx, library_name);
+                    if (lib) {
+                        bh_arr_push(out, lib);
+                    }
+                }
+                break;
+            }
+        }
+
+        cursor = section_start + section_size;
+    }
+
+    *p_out = out;
+}
+
+static void onyx_print_trap(wasm_trap_t* trap) {
+    wasm_message_t msg;
+    wasm_trap_message(trap, &msg);
+    bh_printf("TRAP: %b\n", msg.data, msg.size);
+
+    i32 func_name_section = 0;
+
+    i32 cursor = 8; // skip the magic number and version
+    while (cursor < wasm_raw_bytes.length) {
+        u64 section_number = uleb128_to_uint(wasm_raw_bytes.data, &cursor);
+        u64 section_size   = uleb128_to_uint(wasm_raw_bytes.data, &cursor);
+
+        i32 section_start = cursor;
+        if (section_number == 0) {
+            u64 name_len = uleb128_to_uint(wasm_raw_bytes.data, &cursor);
+            if (!strncmp(wasm_raw_bytes.data + cursor, "_onyx_func_offsets", name_len)) {
+                cursor += name_len;
+                func_name_section = cursor;
+                break;
+            }
+        }
+
+        cursor = section_start + section_size;
+    }
+
+    if (func_name_section == 0) return;
+
+    bh_printf("TRACE:\n");
+    wasm_frame_vec_t frames;
+    wasm_trap_trace(trap, &frames);
+    fori (i, 0, (i32) frames.size) {
+        i32 func_idx   = wasm_frame_func_index(frames.data[i]);
+        i32 mod_offset = wasm_frame_module_offset(frames.data[i]);
+
+        i32 cursor = func_name_section + 4 * func_idx;
+        i32 func_offset = *(i32 *) (wasm_raw_bytes.data + cursor);
+        char* func_name = wasm_raw_bytes.data + func_name_section + func_offset;
+
+        bh_printf("    func[%d]:%p at %s\n", func_idx, mod_offset, func_name);
+    }
+}
+
+static void cleanup_wasm_objects() {
+    if (wasm_instance) wasm_instance_delete(wasm_instance);
+    if (wasm_module) wasm_module_delete(wasm_module);
+    if (wasm_store)  wasm_store_delete(wasm_store);
+    if (wasm_engine) wasm_engine_delete(wasm_engine);
+}
+
+//
+// This could be cleaned up a bit, as this function directly modifies various global variables.
+// Those being wasm_memory and wasm_imports.
+static b32 link_wasm_imports(bh_arr(WasmFuncDefinition **) linkable_functions, wasm_module_t *wasm_module) {
+    wasm_importtype_vec_t module_imports;
+    wasm_module_imports(wasm_module, &module_imports);
+
+    wasm_extern_vec_new_uninitialized(&wasm_imports, module_imports.size); // @Free
+
+    fori (i, 0, (i32) module_imports.size) {
+        const wasm_name_t* module_name = wasm_importtype_module(module_imports.data[i]);
+        const wasm_name_t* import_name = wasm_importtype_name(module_imports.data[i]);
+
+        wasm_extern_t* import = NULL;
+
+        if (wasm_name_equals_string(module_name, "onyx")) {
+            if (wasm_name_equals_string(import_name, "memory")) {
+                if (wasm_memory == NULL) {
+                    wasm_limits_t limits = { 1024, 65536 };
+                    wasm_memorytype_t* memory_type = wasm_memorytype_new(&limits);
+                    wasm_memory = wasm_memory_new(wasm_store, memory_type);
+                }
+
+                import = wasm_memory_as_extern(wasm_memory);
+                goto import_found;
+            }
+        }
+
+        bh_arr_each(WasmFuncDefinition **, library_funcs, linkable_functions) {
+            WasmFuncDefinition **pcurrent_function = *library_funcs;
+            while (*pcurrent_function != NULL) {
+                WasmFuncDefinition *cf = *pcurrent_function;
+                if (wasm_name_equals_string(module_name, cf->module_name) && wasm_name_equals_string(import_name, cf->import_name)) {
+                    wasm_valtype_vec_t wasm_params;
+                    wasm_valtype_vec_new_uninitialized(&wasm_params, cf->params->count);
+                    fori (k, 0, cf->params->count) wasm_params.data[k] = wasm_valtype_new(cf->params->types[k]);
+
+                    wasm_valtype_vec_t wasm_results;
+                    wasm_valtype_vec_new_uninitialized(&wasm_results, cf->results->count);
+                    fori (k, 0, cf->results->count) wasm_results.data[k] = wasm_valtype_new(cf->results->types[k]);
+
+                    wasm_functype_t* wasm_functype = wasm_functype_new(&wasm_params, &wasm_results);
+
+                    wasm_func_t* wasm_func = wasm_func_new(wasm_store, wasm_functype, cf->func);
+                    import = wasm_func_as_extern(wasm_func);
+                    goto import_found;
+                }
+
+                pcurrent_function += 1;
+            }
+        }
+
+        goto bad_import;
+
+    import_found:
+        wasm_imports.data[i] = import;
+        continue;
+
+
+    bad_import:
+        bh_printf("Couldn't find import %b.%b.\n", module_name->data, module_name->size, import_name->data, import_name->size);
+        return 0;
+    }
+
+    return 1;
+}
+
+void onyx_run_initialize(b32 debug_enabled) {
+    wasm_config = wasm_config_new();
+    if (!wasm_config) {
+        cleanup_wasm_objects();
+        return;
+    }
+
+#ifdef USE_OVM_DEBUGGER
+    void wasm_config_enable_debug(wasm_config_t *config, int value);
+    wasm_config_enable_debug(wasm_config, debug_enabled);
+#endif
+
+#ifndef USE_OVM_DEBUGGER
+    if (debug_enabled) {
+        printf("Warning: --debug does nothing if libovmwasm.so is not being used!\n");
+    }
+
+    // Prefer the LLVM compile because it is faster. This should be configurable from the command line and/or a top-level directive.
+    if (wasmer_is_compiler_available(LLVM)) {
+        wasm_config_set_compiler(wasm_config, LLVM);
+    }
+
+    wasmer_features_t* features = wasmer_features_new();
+    wasmer_features_simd(features, 1);
+    wasmer_features_threads(features, 1);
+    wasmer_features_bulk_memory(features, 1);
+    wasm_config_set_features(wasm_config, features);
+#endif
+
+    wasm_engine = wasm_engine_new_with_config(wasm_config);
+    if (!wasm_engine) {
+        cleanup_wasm_objects();
+        return;
+    }
+
+    wasm_store  = wasm_store_new(wasm_engine);
+    if (!wasm_store) {
+        cleanup_wasm_objects();
+        return;
+    }
+
+    // See comment in onyx_library.h about us being the linker.
+    wasm_runtime.wasm_memory_data = &wasm_memory_data;
+    wasm_runtime.wasm_extern_lookup_by_name = &wasm_extern_lookup_by_name;
+    wasm_runtime.wasm_extern_as_func = &wasm_extern_as_func;
+    wasm_runtime.wasm_func_call = &wasm_func_call;
+    wasm_runtime.wasm_instance_new = &wasm_instance_new;
+    wasm_runtime.wasm_store_new = &wasm_store_new;
+    wasm_runtime.wasm_store_delete = &wasm_store_delete;
+    wasm_runtime.onyx_print_trap = &onyx_print_trap;
+}
+
+b32 onyx_run_wasm(bh_buffer wasm_bytes, int argc, char *argv[]) {
+    runtime = &wasm_runtime;
+
+    if (strncmp(wasm_bytes.data, "ONYX", 4)) {
+        printf("Bad magic bytes for Onyx binary.\n");
+        return 0;
+    }
+
+    wasm_bytes.data[0] = '\0';
+    wasm_bytes.data[1] = 'a';
+    wasm_bytes.data[2] = 's';
+    wasm_bytes.data[3] = 'm';
+    wasm_raw_bytes = wasm_bytes;
+
+    bh_arr(WasmFuncDefinition **) linkable_functions = NULL;
+    bh_arr_new(bh_heap_allocator(), linkable_functions, 4);
+
+    {
+        LibraryLinkContext lib_ctx;
+        lib_ctx.wasm_bytes = wasm_bytes;
+        lib_ctx.library_paths = NULL;
+        lookup_and_load_custom_libraries(&lib_ctx, &linkable_functions);
+
+        bh_arr_free(lib_ctx.library_paths);
+    }
+
+    wasm_byte_vec_t wasm_data;
+    wasm_data.size = wasm_bytes.length;
+    wasm_data.data = wasm_bytes.data;
+
+    wasm_module = wasm_module_new(wasm_store, &wasm_data);
+    if (!wasm_module) {
+        cleanup_wasm_objects();
+        return 0;
+    }
+
+    wasm_imports = (wasm_extern_vec_t) WASM_EMPTY_VEC;
+    if (!link_wasm_imports(linkable_functions, wasm_module)) {
+        return 0;
+    }
+
+    wasm_trap_t* traps = NULL;
+
+    wasm_instance = wasm_instance_new(wasm_store, wasm_module, &wasm_imports, &traps);
+    if (!wasm_instance) {
+        cleanup_wasm_objects();
+        return 0;
+    }
+
+    wasm_runtime.wasm_engine = wasm_engine;
+    wasm_runtime.wasm_module = wasm_module;
+    wasm_runtime.wasm_imports = wasm_imports;
+    wasm_runtime.wasm_memory = wasm_memory;
+    wasm_runtime.wasm_instance = wasm_instance;
+    
+    wasm_runtime.argc = argc;
+    wasm_runtime.argv = argv;
+
+    wasm_extern_t* start_extern = wasm_extern_lookup_by_name(wasm_module, wasm_instance, "_start");
+    wasm_func_t*   start_func   = wasm_extern_as_func(start_extern);
+
+    wasm_val_vec_t args;
+    wasm_val_vec_t results;
+    wasm_val_vec_new_uninitialized(&args, 0);
+    wasm_val_vec_new_uninitialized(&results, 1);
+
+    wasm_trap_t *run_trap = wasm_func_call(start_func, &args, &results);
+
+#if 1
+    if (run_trap != NULL) onyx_print_trap(run_trap);
+#endif
+
+    goto end;
+
+    bh_printf("An error occured trying to run the WASM module...\n");
+
+#ifndef USE_OVM_DEBUGGER
+    i32 len = wasmer_last_error_length();
+    char *buf = alloca(len + 1);
+    wasmer_last_error_message(buf, len);
+    bh_printf("%b\n", buf, len);
+#endif
+
+end:
+    cleanup_wasm_objects();
+    return run_trap == NULL;
+}
diff --git a/compiler/src/wasm_type_table.h b/compiler/src/wasm_type_table.h
new file mode 100644 (file)
index 0000000..2cb1c67
--- /dev/null
@@ -0,0 +1,902 @@
+// This file is directly included in src/onxywasm.c
+// It is here purely to decrease the amount of clutter in the main file.
+
+typedef struct StructMethodData {
+    u32 name_loc;
+    u32 name_len;
+    u32 type;
+    u32 data_loc;
+} StructMethodData;
+
+static u64 build_type_table(OnyxWasmModule* module) {
+
+    bh_arr(u32) base_patch_locations=NULL;
+    bh_arr_new(global_heap_allocator, base_patch_locations, 256);
+
+#define PATCH (bh_arr_push(base_patch_locations, table_buffer.length))
+#define WRITE_PTR(val) \
+    bh_buffer_align(&table_buffer, POINTER_SIZE); \
+    PATCH; \
+    if (POINTER_SIZE == 4) bh_buffer_write_u32(&table_buffer, val); \
+    if (POINTER_SIZE == 8) bh_buffer_write_u64(&table_buffer, val); 
+#define WRITE_SLICE(ptr, count) \
+    WRITE_PTR(ptr); \
+    if (POINTER_SIZE == 4) bh_buffer_write_u32(&table_buffer, count); \
+    if (POINTER_SIZE == 8) bh_buffer_write_u64(&table_buffer, count); 
+
+    // This is the data behind the "type_table" slice in runtime/info/types.onyx
+    #if (POINTER_SIZE == 4)
+        #define Table_Info_Type u32
+    #else
+        #define Table_Info_Type u64
+    #endif
+    u32 type_count = bh_arr_length(type_map.entries) + 1;
+    Table_Info_Type* table_info = bh_alloc_array(global_heap_allocator, Table_Info_Type, type_count); // HACK
+    memset(table_info, 0, type_count * sizeof(Table_Info_Type));
+
+    bh_buffer table_buffer;
+    bh_buffer_init(&table_buffer, global_heap_allocator, 4096);
+
+    u32 type_table_info_data_id = NEXT_DATA_ID(module);
+
+    ConstExprContext constexpr_ctx;
+    constexpr_ctx.module = module;
+    constexpr_ctx.data_id = type_table_info_data_id;
+
+    // Write a "NULL" at the beginning so nothing will have to point to the first byte of the buffer.
+    bh_buffer_write_u64(&table_buffer, 0);
+
+    bh_arr_each(bh__imap_entry, type_entry, type_map.entries) {
+        u64 type_idx = type_entry->key;
+        Type* type = (Type *) type_entry->value;
+
+        switch (type->kind) {
+            case Type_Kind_Basic: {
+                table_info[type_idx] = table_buffer.length;
+                bh_buffer_write_u32(&table_buffer, type->kind);
+                bh_buffer_write_u32(&table_buffer, type_size_of(type));
+                bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
+                bh_buffer_write_u32(&table_buffer, type->Basic.kind);
+                break;
+            }
+
+            case Type_Kind_Pointer: {
+                table_info[type_idx] = table_buffer.length;
+                bh_buffer_write_u32(&table_buffer, type->kind);
+                bh_buffer_write_u32(&table_buffer, type_size_of(type));
+                bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
+                bh_buffer_write_u32(&table_buffer, type->Pointer.elem->id);
+                break;
+            }
+
+            case Type_Kind_Array: {
+                table_info[type_idx] = table_buffer.length;
+                bh_buffer_write_u32(&table_buffer, type->kind);
+                bh_buffer_write_u32(&table_buffer, type_size_of(type));
+                bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
+                bh_buffer_write_u32(&table_buffer, type->Array.elem->id);
+                bh_buffer_write_u32(&table_buffer, type->Array.count);
+                break;
+            }
+
+            case Type_Kind_Slice: {
+                table_info[type_idx] = table_buffer.length;
+                bh_buffer_write_u32(&table_buffer, type->kind);
+                bh_buffer_write_u32(&table_buffer, type_size_of(type));
+                bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
+                bh_buffer_write_u32(&table_buffer, type->Slice.elem->id);
+                break;
+            }
+
+            case Type_Kind_DynArray: {
+                table_info[type_idx] = table_buffer.length;
+                bh_buffer_write_u32(&table_buffer, type->kind);
+                bh_buffer_write_u32(&table_buffer, type_size_of(type));
+                bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
+                bh_buffer_write_u32(&table_buffer, type->DynArray.elem->id);
+                break;
+            }
+
+            case Type_Kind_VarArgs: {
+                table_info[type_idx] = table_buffer.length;
+                bh_buffer_write_u32(&table_buffer, type->kind);
+                bh_buffer_write_u32(&table_buffer, type_size_of(type));
+                bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
+                bh_buffer_write_u32(&table_buffer, type->VarArgs.elem->id);
+                break;
+            }
+
+            case Type_Kind_Compound: {
+                u32 components_base = table_buffer.length;
+
+                u32 components_count = type->Compound.count;
+                fori (i, 0, components_count) {
+                    u32 type_idx = type->Compound.types[i]->id;
+                    bh_buffer_write_u32(&table_buffer, type_idx);
+                }
+
+                bh_buffer_align(&table_buffer, 8);
+                table_info[type_idx] = table_buffer.length;
+                bh_buffer_write_u32(&table_buffer, type->kind);
+                bh_buffer_write_u32(&table_buffer, type_size_of(type));
+                bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
+                WRITE_SLICE(components_base, components_count);
+                break;
+            }
+
+            case Type_Kind_Function: {
+                u32 parameters_base = table_buffer.length;
+
+                u32 parameters_count = type->Function.param_count;
+                fori (i, 0, parameters_count) {
+                    u32 type_idx = type->Function.params[i]->id;
+                    bh_buffer_write_u32(&table_buffer, type_idx);
+                }
+
+                table_info[type_idx] = table_buffer.length;
+                bh_buffer_write_u32(&table_buffer, type->kind);
+                bh_buffer_write_u32(&table_buffer, type_size_of(type));
+                bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
+                bh_buffer_write_u32(&table_buffer, type->Function.return_type->id);
+
+                WRITE_SLICE(parameters_base, parameters_count);
+
+                bh_buffer_write_u32(&table_buffer, type->Function.vararg_arg_pos > 0 ? 1 : 0);
+                break;
+            }
+
+            case Type_Kind_Enum: {
+                AstEnumType* ast_enum = (AstEnumType *) type->ast_type;
+                u32 member_count = bh_arr_length(ast_enum->values);
+                u32* name_locations = bh_alloc_array(global_scratch_allocator, u32, member_count);
+
+                u32 i = 0;
+                bh_arr_each(AstEnumValue *, value, ast_enum->values) {
+                    name_locations[i++] = table_buffer.length;
+
+                    bh_buffer_append(&table_buffer, (*value)->token->text, (*value)->token->length);
+                }
+                bh_buffer_align(&table_buffer, 8);
+
+                u32 member_base = table_buffer.length;
+                i = 0;
+                bh_arr_each(AstEnumValue *, value, ast_enum->values) {
+                    u32 name_loc = name_locations[i++];
+
+                    bh_buffer_align(&table_buffer, 8);
+                    WRITE_SLICE(name_loc, (*value)->token->length);
+
+                    assert((*value)->value->kind == Ast_Kind_NumLit);
+                    AstNumLit *num = (AstNumLit *) (*value)->value;
+                    bh_buffer_write_u64(&table_buffer, num->value.l);
+                }
+
+                u32 name_base = table_buffer.length;
+                u32 name_length = strlen(type->Enum.name);
+                bh_buffer_append(&table_buffer, type->Enum.name, name_length);
+                bh_buffer_align(&table_buffer, 8);
+
+                table_info[type_idx] = table_buffer.length;
+                bh_buffer_write_u32(&table_buffer, type->kind);
+                bh_buffer_write_u32(&table_buffer, type_size_of(type));
+                bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
+                bh_buffer_write_u32(&table_buffer, type->Enum.backing->id);
+                WRITE_SLICE(name_base, name_length);
+                WRITE_SLICE(member_base, member_count);
+                bh_buffer_write_u32(&table_buffer, type->Enum.is_flags ? 1 : 0);
+                break;
+            }
+
+            case Type_Kind_Struct: {
+                TypeStruct* s = &type->Struct;
+                u32* name_locations = bh_alloc_array(global_scratch_allocator, u32, s->mem_count);
+                u32* param_locations = bh_alloc_array(global_scratch_allocator, u32, bh_arr_length(s->poly_sln));
+                u32* value_locations = bh_alloc_array(global_scratch_allocator, u32, s->mem_count);
+                u32* meta_locations = bh_alloc_array(global_scratch_allocator, u32, s->mem_count);
+                u32* struct_tag_locations = bh_alloc_array(global_scratch_allocator, u32, bh_arr_length(s->meta_tags));
+                memset(value_locations, 0, s->mem_count * sizeof(u32));
+                memset(meta_locations, 0, s->mem_count * sizeof(u32));
+                memset(struct_tag_locations, 0, bh_arr_length(s->meta_tags) * sizeof(u32));
+
+                // Member names
+                u32 i = 0;
+                bh_arr_each(StructMember*, pmem, s->memarr) {
+                    StructMember* mem = *pmem;
+
+                    name_locations[i++] = table_buffer.length;
+                    bh_buffer_append(&table_buffer, mem->name, strlen(mem->name));
+                }
+
+                bh_buffer_align(&table_buffer, 8);
+
+                // Polymorphic solutions
+                i = 0;
+                bh_arr_each(AstPolySolution, sln, s->poly_sln) {
+                    bh_buffer_align(&table_buffer, 8);
+                    param_locations[i++] = table_buffer.length;
+
+                    switch (sln->kind) {
+                        case PSK_Type: {
+                            // NOTE: This assumes a little endian compiler (which is assumed in other part of the code too)
+                            bh_buffer_append(&table_buffer, &sln->type->id, 4);
+                            break;
+                        }
+
+                        case PSK_Value: {
+                            assert(sln->value->type);
+                            u32 size = type_size_of(sln->value->type);
+
+                            bh_buffer_grow(&table_buffer, table_buffer.length + size);
+                            constexpr_ctx.data = table_buffer.data;
+                            if (emit_constexpr_(&constexpr_ctx, sln->value, table_buffer.length)) {
+                                table_buffer.length += size;
+                                break;
+                            }
+
+                            // fallthrough
+                        }
+
+                        default: {
+                            // Set to null if this is not known how to encode
+                            param_locations[i-1] = 0;
+                            break;
+                        }
+                    }
+                }
+
+                bh_buffer_align(&table_buffer, 8);
+
+                // Member default values
+                i = 0;
+                bh_arr_each(StructMember*, pmem, s->memarr) {
+                    StructMember* mem = *pmem;
+
+                    if (mem->initial_value == NULL || *mem->initial_value == NULL) {
+                        i++;
+                        continue;
+                    }
+
+                    AstTyped* value = *mem->initial_value;
+                    assert(value->type);
+
+                    if ((value->flags & Ast_Flag_Comptime) == 0) {
+                        // onyx_report_warning(value->token->pos, "Warning: skipping generating default value for '%s' in '%s' because it is not compile-time known.\n", mem->name, s->name);
+                        i++;
+                        continue;
+                    }
+
+                    u32 size = type_size_of(value->type);
+                    bh_buffer_align(&table_buffer, type_alignment_of(value->type));
+
+                    bh_buffer_grow(&table_buffer, table_buffer.length + size);
+                    constexpr_ctx.data = table_buffer.data;
+                    if (!emit_constexpr_(&constexpr_ctx, value, table_buffer.length)) {
+                        // Failed to generate raw data
+                        // onyx_report_warning(value->token->pos, "Warning: failed to generate default value for '%s' in '%s'.\n", mem->name, s->name);
+                        value_locations[i++] = 0;
+
+                    } else {
+                        // Success 
+                        value_locations[i++] = table_buffer.length;
+                        table_buffer.length += size;
+                    }
+                }
+
+                // Member tags
+                i = 0;
+                bh_arr_each(StructMember*, pmem, s->memarr) {
+                    StructMember* mem = *pmem;
+
+                    if (mem->meta_tags == NULL) {
+                        i += 1;
+                        continue;
+                    }
+
+                    bh_arr(AstTyped *) meta_tags = mem->meta_tags;
+                    assert(meta_tags);
+
+                    bh_arr(u64) meta_tag_locations=NULL;
+                    bh_arr_new(global_heap_allocator, meta_tag_locations, bh_arr_length(meta_tags));
+
+                    int j = 0;
+                    bh_arr_each(AstTyped *, meta, meta_tags) {
+                        AstTyped* value = *meta;                        
+                        assert(value->flags & Ast_Flag_Comptime);
+                        assert(value->type);
+
+                        u32 size = type_size_of(value->type);
+                        bh_buffer_align(&table_buffer, type_alignment_of(value->type));
+                        meta_tag_locations[j] = table_buffer.length;
+
+                        bh_buffer_grow(&table_buffer, table_buffer.length + size);
+                        constexpr_ctx.data = table_buffer.data;
+                        assert(emit_constexpr_(&constexpr_ctx, value, table_buffer.length));
+                        table_buffer.length += size;
+
+                        j += 1;
+                    }
+
+                    bh_buffer_align(&table_buffer, 8);
+                    meta_locations[i] = table_buffer.length;
+
+                    fori (k, 0, bh_arr_length(meta_tags)) {
+                        WRITE_SLICE(meta_tag_locations[k], meta_tags[k]->type->id);
+                    }
+
+                    bh_arr_free(meta_tag_locations);
+                    i += 1;
+                }
+
+                bh_buffer_align(&table_buffer, 8);
+                u32 members_base = table_buffer.length;
+
+                // Member array
+                i = 0;
+                bh_arr_each(StructMember*, pmem, s->memarr) {
+                    StructMember* mem = *pmem;
+
+                    u32 name_loc = name_locations[i];
+                    u32 value_loc = value_locations[i];
+                    u32 meta_loc = meta_locations[i++];
+
+                    WRITE_SLICE(name_loc, strlen(mem->name));
+                    bh_buffer_write_u32(&table_buffer, mem->offset);
+                    bh_buffer_write_u32(&table_buffer, mem->type->id);
+                    bh_buffer_write_byte(&table_buffer, mem->used ? 1 : 0);
+                    
+                    WRITE_PTR(value_loc);
+
+                    WRITE_SLICE(meta_loc, bh_arr_length(mem->meta_tags));
+                }
+
+                bh_buffer_align(&table_buffer, 8);
+                u32 params_base = table_buffer.length;
+
+                // Polymorphic solution any array
+                i = 0;
+                bh_arr_each(AstPolySolution, sln, s->poly_sln) {
+                    WRITE_PTR(param_locations[i++]);
+
+                    if (sln->kind == PSK_Type) bh_buffer_write_u32(&table_buffer, basic_types[Basic_Kind_Type_Index].id);
+                    else                       bh_buffer_write_u32(&table_buffer, sln->value->type->id);
+                }
+
+                // Struct tag array
+                i = 0;
+                bh_arr_each(AstTyped *, tag, s->meta_tags) {
+                    AstTyped* value = *tag;                        
+                    assert(value->flags & Ast_Flag_Comptime);
+                    assert(value->type);
+
+                    u32 size = type_size_of(value->type);
+                    bh_buffer_align(&table_buffer, type_alignment_of(value->type));
+                    struct_tag_locations[i] = table_buffer.length;
+
+                    bh_buffer_grow(&table_buffer, table_buffer.length + size);
+                    constexpr_ctx.data = table_buffer.data;
+                    assert(emit_constexpr_(&constexpr_ctx, value, table_buffer.length));
+                    table_buffer.length += size;
+
+                    i += 1;
+                }
+
+                // Struct methods
+                bh_arr(StructMethodData) method_data=NULL;
+                AstType *ast_type = type->ast_type;
+                if (ast_type && ast_type->kind == Ast_Kind_Struct_Type) {
+                    AstStructType *struct_type  = (AstStructType *) ast_type;
+                    Scope*         struct_scope = struct_type->scope;
+
+                    if (struct_scope == NULL) goto no_methods;
+
+                    fori (i, 0, shlen(struct_scope->symbols)) {
+                        AstFunction* node = (AstFunction *) strip_aliases(struct_scope->symbols[i].value);
+                        if (node->kind != Ast_Kind_Function) continue;
+                        assert(node->entity);
+                        assert(node->entity->function == node);
+
+                        // Name
+                        char *name = struct_scope->symbols[i].key;
+                        u32 name_loc = table_buffer.length;
+                        u32 name_len = strlen(name);
+                        bh_buffer_append(&table_buffer, name, name_len);
+
+                        // any data member
+                        bh_buffer_align(&table_buffer, 4);
+                        u32 data_loc = table_buffer.length;
+                        u32 func_idx = get_element_idx(module, node);
+                        bh_buffer_write_u32(&table_buffer, func_idx);
+                        
+                        bh_arr_push(method_data, ((StructMethodData) {
+                            .name_loc = name_loc,
+                            .name_len = name_len,
+                            .type     = node->type->id,
+                            .data_loc = data_loc,
+                        }));
+                    }
+                }
+
+                no_methods:
+
+                bh_buffer_align(&table_buffer, 4);
+                u32 method_data_base = table_buffer.length;
+
+                i = 0;
+                bh_arr_each(StructMethodData, method, method_data) {
+                    WRITE_SLICE(method->name_loc, method->name_len);
+                    WRITE_PTR(method->data_loc); 
+                    bh_buffer_write_u32(&table_buffer, method->type);
+                }
+
+                bh_buffer_align(&table_buffer, 8);
+                u32 struct_tag_base = table_buffer.length;
+
+                fori (i, 0, bh_arr_length(s->meta_tags)) {
+                    WRITE_SLICE(struct_tag_locations[i], s->meta_tags[i]->type->id);
+                }
+
+                // Struct name
+                u32 name_base = 0;
+                u32 name_length = 0;
+                if (s->name) {
+                    name_length = strlen(s->name);
+                    name_base = table_buffer.length;
+                    bh_buffer_append(&table_buffer, s->name, name_length);
+                }
+
+                bh_buffer_align(&table_buffer, 8);
+                table_info[type_idx] = table_buffer.length;
+                bh_buffer_write_u32(&table_buffer, type->kind);
+                bh_buffer_write_u32(&table_buffer, type_size_of(type));
+                bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
+
+                if (type->Struct.constructed_from != NULL) {
+                    bh_buffer_write_u32(&table_buffer, type->Struct.constructed_from->type_id);
+                } else {
+                    bh_buffer_write_u32(&table_buffer, 0);
+                }
+
+                WRITE_SLICE(name_base, name_length);
+                WRITE_SLICE(members_base, s->mem_count);
+                WRITE_SLICE(params_base, bh_arr_length(s->poly_sln));
+                WRITE_SLICE(struct_tag_base, bh_arr_length(s->meta_tags));
+                WRITE_SLICE(method_data_base, bh_arr_length(method_data));
+
+                bh_arr_free(method_data);
+                break;
+            }
+
+            case Type_Kind_PolyStruct: {
+                u32* tag_locations = bh_alloc_array(global_scratch_allocator, u32, bh_arr_length(type->PolyStruct.meta_tags));
+                memset(tag_locations, 0, sizeof(u32) * bh_arr_length(type->PolyStruct.meta_tags));
+
+                u32 name_base = table_buffer.length;
+                u32 name_length = strlen(type->PolyStruct.name);
+                bh_buffer_append(&table_buffer, type->PolyStruct.name, name_length);
+
+                u32 tags_count = bh_arr_length(type->PolyStruct.meta_tags);
+                i32 i = 0;
+                bh_arr_each(AstTyped *, tag, type->PolyStruct.meta_tags) {
+                    AstTyped* value = *tag;                        
+
+                    // Polymorphic structs are weird in this case, because the tag might not be constructed generically for
+                    // the polymorphic structure so it should only be constructed for actual solidified structures.
+                    // See core/containers/map.onyx with Custom_Format for an example.
+                    if (!(value->flags & Ast_Flag_Comptime)) {
+                        tags_count--;
+                        continue;
+                    }
+
+                    assert(value->type);
+
+                    u32 size = type_size_of(value->type);
+                    bh_buffer_align(&table_buffer, type_alignment_of(value->type));
+                    tag_locations[i] = table_buffer.length;
+
+                    bh_buffer_grow(&table_buffer, table_buffer.length + size);
+
+                    constexpr_ctx.data = table_buffer.data;
+                    assert(emit_constexpr_(&constexpr_ctx, value, table_buffer.length));
+                    table_buffer.length += size;
+
+                    i += 1;
+                }
+
+                bh_buffer_align(&table_buffer, 8);
+                u32 tags_base = table_buffer.length;
+
+                fori (i, 0, tags_count) {
+                    WRITE_SLICE(tag_locations[i], type->PolyStruct.meta_tags[i]->type->id);
+                }
+
+                bh_buffer_align(&table_buffer, 8);
+                table_info[type_idx] = table_buffer.length;
+                bh_buffer_write_u32(&table_buffer, type->kind);
+                bh_buffer_write_u32(&table_buffer, 0);
+                bh_buffer_write_u32(&table_buffer, 0);
+                WRITE_SLICE(name_base, name_length);
+                WRITE_SLICE(tags_base, tags_count);
+
+                break;
+            }
+        
+            case Type_Kind_Distinct: {
+                u32 name_base = table_buffer.length;
+                u32 name_length = strlen(type->Distinct.name);
+                bh_buffer_append(&table_buffer, type->Distinct.name, name_length);
+                bh_buffer_align(&table_buffer, 8);
+
+                table_info[type_idx] = table_buffer.length;
+                bh_buffer_write_u32(&table_buffer, type->kind);
+                bh_buffer_write_u32(&table_buffer, type_size_of(type));
+                bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
+                bh_buffer_write_u32(&table_buffer, type->Distinct.base_type->id);
+                WRITE_SLICE(name_base, name_length);
+                break;
+            }
+        }
+    }
+
+    if (context.options->verbose_output == 1) {
+        bh_printf("Type table size: %d bytes.\n", table_buffer.length);
+    }
+
+    WasmDatum type_info_data = {
+        .alignment = 8,
+        .length = table_buffer.length,
+        .data = table_buffer.data,
+    };
+    emit_data_entry(module, &type_info_data);
+    assert(type_info_data.id == type_table_info_data_id);
+
+    bh_arr_each(u32, patch_loc, base_patch_locations) {
+        DatumPatchInfo patch;
+        patch.kind = Datum_Patch_Relative;
+        patch.data_id = type_info_data.id;
+        patch.offset = 0;
+        patch.index = type_info_data.id;
+        patch.location = *patch_loc;
+        bh_arr_push(module->data_patches, patch);
+    }
+
+    WasmDatum type_table_data = {
+        .alignment = POINTER_SIZE,
+        .length = type_count * POINTER_SIZE,
+        .data = table_info,
+    };
+    emit_data_entry(module, &type_table_data);
+
+    fori (i, 0, type_count) {
+        DatumPatchInfo patch;
+        patch.kind = Datum_Patch_Data;
+        patch.data_id = type_info_data.id;
+        patch.offset = table_info[i];
+        patch.index = type_table_data.id;
+        patch.location = i * POINTER_SIZE;
+        bh_arr_push(module->data_patches, patch);
+    }
+
+    Table_Info_Type* tmp_data = bh_alloc(global_heap_allocator, 2 * POINTER_SIZE);
+    tmp_data[0] = 0;
+    tmp_data[1] = type_count;
+    WasmDatum type_table_global_data = {
+        .alignment = POINTER_SIZE,
+        .length = 2 * POINTER_SIZE,
+        .data = tmp_data,
+    };
+    emit_data_entry(module, &type_table_global_data);
+
+    {
+        DatumPatchInfo patch;
+        patch.kind = Datum_Patch_Data;
+        patch.data_id = type_table_data.id;
+        patch.offset = 0;
+        patch.index = type_table_global_data.id;
+        patch.location = 0;
+        bh_arr_push(module->data_patches, patch);
+    }
+
+    return type_table_global_data.id;
+
+#undef WRITE_SLICE
+#undef WRITE_PTR
+#undef PATCH
+}
+
+
+
+static u64 build_foreign_blocks(OnyxWasmModule* module) {
+    bh_arr(u32) base_patch_locations=NULL;
+    bh_arr_new(global_heap_allocator, base_patch_locations, 256);
+
+#define PATCH (bh_arr_push(base_patch_locations, foreign_buffer.length))
+#define WRITE_PTR(val) \
+    bh_buffer_align(&foreign_buffer, POINTER_SIZE); \
+    PATCH; \
+    if (POINTER_SIZE == 4) bh_buffer_write_u32(&foreign_buffer, val); \
+    if (POINTER_SIZE == 8) bh_buffer_write_u64(&foreign_buffer, val); 
+#define WRITE_SLICE(ptr, count) \
+    WRITE_PTR(ptr); \
+    if (POINTER_SIZE == 4) bh_buffer_write_u32(&foreign_buffer, count); \
+    if (POINTER_SIZE == 8) bh_buffer_write_u64(&foreign_buffer, count); 
+
+    // This is the data behind the "type_table" slice in type_info.onyx
+    #if (POINTER_SIZE == 4)
+        #define Foreign_Block_Type u32
+    #else
+        #define Foreign_Block_Type u64
+    #endif
+    u32 block_count = bh_arr_length(module->foreign_blocks);
+    Foreign_Block_Type* foreign_info = bh_alloc_array(global_heap_allocator, Foreign_Block_Type, block_count); // HACK
+    memset(foreign_info, 0, block_count * sizeof(Foreign_Block_Type));
+
+    bh_buffer foreign_buffer;
+    bh_buffer_init(&foreign_buffer, global_heap_allocator, 4096);
+
+    // 
+    // This is necessary because 0 is an invalid offset to store in this
+    // buffer, as 0 will map to NULL. This could be a single byte insertion,
+    // but 64 bytes keeps better alignment.
+    bh_buffer_write_u64(&foreign_buffer, 0);
+
+    u32 index = 0;
+    bh_arr_each(AstForeignBlock *, pfb, module->foreign_blocks) {
+        AstForeignBlock *fb = *pfb;
+
+        u32 funcs_length = 0;
+
+        u32 *name_offsets = bh_alloc_array(global_scratch_allocator, u32, shlen(fb->scope->symbols));
+        u32 *name_lengths = bh_alloc_array(global_scratch_allocator, u32, shlen(fb->scope->symbols));
+        u32 *func_types   = bh_alloc_array(global_scratch_allocator, u32, shlen(fb->scope->symbols));
+
+        fori (i, 0, shlen(fb->scope->symbols)) {
+            AstFunction *func = (AstFunction *) fb->scope->symbols[i].value;
+            if (func->kind != Ast_Kind_Function) continue;
+
+            u32 func_name_base = foreign_buffer.length;
+            u32 func_name_length = func->foreign_name->length;
+            bh_buffer_append(&foreign_buffer, func->foreign_name->text, func_name_length);
+
+            name_offsets[funcs_length] = func_name_base;
+            name_lengths[funcs_length] = func_name_length;
+            func_types[funcs_length]   = func->type->id;
+            funcs_length++;
+        }
+
+        bh_buffer_align(&foreign_buffer, 8);
+        u32 funcs_base = foreign_buffer.length;
+
+        fori (i, 0, (i64) funcs_length) {
+            bh_buffer_align(&foreign_buffer, POINTER_SIZE);
+            WRITE_SLICE(name_offsets[i], name_lengths[i]);
+            bh_buffer_write_u32(&foreign_buffer, func_types[i]);
+        }
+
+        u32 name_base = foreign_buffer.length;
+        u32 name_length = fb->module_name->length;
+        bh_buffer_append(&foreign_buffer, fb->module_name->text, name_length);
+        bh_buffer_align(&foreign_buffer, 8);
+
+        foreign_info[index] = foreign_buffer.length;
+        WRITE_SLICE(name_base, name_length);
+        WRITE_SLICE(funcs_base, funcs_length);
+        index++;
+    }
+
+
+    if (context.options->verbose_output == 1) {
+        bh_printf("Foreign blocks size: %d bytes.\n", foreign_buffer.length);
+    }
+
+    WasmDatum foreign_info_data = {
+        .alignment = 8,
+        .length = foreign_buffer.length,
+        .data = foreign_buffer.data,
+    };
+    emit_data_entry(module, &foreign_info_data);
+
+    bh_arr_each(u32, patch_loc, base_patch_locations) {
+        DatumPatchInfo patch;
+        patch.kind = Datum_Patch_Relative;
+        patch.data_id = foreign_info_data.id;
+        patch.offset = 0;
+        patch.index = foreign_info_data.id;
+        patch.location = *patch_loc;
+        bh_arr_push(module->data_patches, patch);
+    }
+
+    WasmDatum foreign_table_data = {
+        .alignment = POINTER_SIZE,
+        .length = block_count * POINTER_SIZE,
+        .data = foreign_info,
+    };
+    emit_data_entry(module, &foreign_table_data);
+
+    fori (i, 0, block_count) {
+        DatumPatchInfo patch;
+        patch.kind = Datum_Patch_Data;
+        patch.data_id = foreign_info_data.id;
+        patch.offset = foreign_info[i];
+        patch.index = foreign_table_data.id;
+        patch.location = i * POINTER_SIZE;
+        bh_arr_push(module->data_patches, patch);
+    }
+
+    Foreign_Block_Type* tmp_data = bh_alloc(global_heap_allocator, 2 * POINTER_SIZE);
+    tmp_data[0] = 0;
+    tmp_data[1] = block_count;
+    WasmDatum foreign_table_global_data = {
+        .alignment = POINTER_SIZE,
+        .length = 2 * POINTER_SIZE,
+        .data = tmp_data,
+    };
+    emit_data_entry(module, &foreign_table_global_data);
+
+    {
+        DatumPatchInfo patch;
+        patch.kind = Datum_Patch_Data;
+        patch.data_id = foreign_table_data.id;
+        patch.location = 0;
+        patch.index = foreign_table_global_data.id;
+        patch.offset = 0;
+        bh_arr_push(module->data_patches, patch);
+    }
+
+    return foreign_table_global_data.id;
+
+#undef WRITE_SLICE
+#undef WRITE_PTR
+#undef PATCH
+}
+
+
+
+static u64 build_tagged_procedures(OnyxWasmModule *module) {
+    bh_arr(u32) base_patch_locations=NULL;
+    bh_arr_new(global_heap_allocator, base_patch_locations, 256);
+
+#define PATCH (bh_arr_push(base_patch_locations, tag_proc_buffer.length))
+#define WRITE_PTR(val) \
+    bh_buffer_align(&tag_proc_buffer, POINTER_SIZE); \
+    PATCH; \
+    if (POINTER_SIZE == 4) bh_buffer_write_u32(&tag_proc_buffer, val); \
+    if (POINTER_SIZE == 8) bh_buffer_write_u64(&tag_proc_buffer, val); 
+#define WRITE_SLICE(ptr, count) \
+    WRITE_PTR(ptr); \
+    if (POINTER_SIZE == 4) bh_buffer_write_u32(&tag_proc_buffer, count); \
+    if (POINTER_SIZE == 8) bh_buffer_write_u64(&tag_proc_buffer, count); 
+
+    #if (POINTER_SIZE == 4)
+        #define Tagged_Procedure_Type u32
+    #else
+        #define Tagged_Procedure_Type u64
+    #endif
+    u32 proc_count = bh_arr_length(module->procedures_with_tags);
+    Tagged_Procedure_Type* tag_proc_info = bh_alloc_array(global_heap_allocator, Tagged_Procedure_Type, proc_count); // HACK
+    memset(tag_proc_info, 0, proc_count * sizeof(Tagged_Procedure_Type));
+
+    bh_buffer tag_proc_buffer;
+    bh_buffer_init(&tag_proc_buffer, global_heap_allocator, 4096);
+
+    u32 proc_info_data_id = NEXT_DATA_ID(module);
+
+    ConstExprContext constexpr_ctx;
+    constexpr_ctx.module = module;
+    constexpr_ctx.data_id = proc_info_data_id;
+
+    // 
+    // This is necessary because 0 is an invalid offset to store in this
+    // buffer, as 0 will map to NULL. This could be a single byte insertion,
+    // but 64 bytes keeps better alignment.
+    bh_buffer_write_u64(&tag_proc_buffer, 0);
+
+    u32 index = 0;
+    bh_arr_each(AstFunction *, pfunc, module->procedures_with_tags) {
+        AstFunction *func = *pfunc;
+        if (!should_emit_function(func)) {
+            proc_count--;
+            continue;
+        }
+
+        u32 tag_count = bh_arr_length(func->tags);
+        u32 *tag_data_offsets = bh_alloc_array(global_scratch_allocator, u32, tag_count);
+        u32 *tag_data_types   = bh_alloc_array(global_scratch_allocator, u32, tag_count);
+
+        u32 tag_index = 0;
+        bh_arr_each(AstTyped *, ptag, func->tags) {
+            AstTyped *tag = *ptag;
+            bh_buffer_align(&tag_proc_buffer, type_alignment_of(tag->type));
+
+            tag_data_offsets[tag_index  ] = tag_proc_buffer.length;
+            tag_data_types  [tag_index++] = tag->type->id;
+
+            u32 size = type_size_of(tag->type);
+            bh_buffer_grow(&tag_proc_buffer, tag_proc_buffer.length + size);
+
+            constexpr_ctx.data = tag_proc_buffer.data;
+            emit_constexpr(&constexpr_ctx, tag, tag_proc_buffer.length);
+            tag_proc_buffer.length += size;
+        }
+
+        bh_buffer_align(&tag_proc_buffer, 4);
+        u32 tag_array_base = tag_proc_buffer.length;
+        fori (i, 0, tag_count) {
+            PATCH;
+            bh_buffer_write_u32(&tag_proc_buffer, tag_data_offsets[i]);
+            bh_buffer_write_u32(&tag_proc_buffer, tag_data_types[i]);
+        }
+
+        bh_buffer_align(&tag_proc_buffer, 4);
+        tag_proc_info[index++] = tag_proc_buffer.length;
+
+        bh_buffer_write_u32(&tag_proc_buffer, get_element_idx(module, func));
+        bh_buffer_write_u32(&tag_proc_buffer, func->type->id);
+        WRITE_SLICE(tag_array_base, tag_count);
+    }    
+
+    if (context.options->verbose_output == 1) {
+        bh_printf("Tagged procedure size: %d bytes.\n", tag_proc_buffer.length);
+    }
+
+    WasmDatum proc_info_data = {
+        .alignment = 8,
+        .length = tag_proc_buffer.length,
+        .data = tag_proc_buffer.data,
+    };
+    emit_data_entry(module, &proc_info_data);
+    assert(proc_info_data.id == proc_info_data_id);
+
+    bh_arr_each(u32, patch_loc, base_patch_locations) {
+        DatumPatchInfo patch;
+        patch.kind = Datum_Patch_Relative;
+        patch.data_id = proc_info_data.id;
+        patch.location = *patch_loc;
+        patch.index = proc_info_data.id;
+        patch.offset = 0;
+        bh_arr_push(module->data_patches, patch);
+    }
+
+    WasmDatum proc_table_data = {
+        .alignment = POINTER_SIZE,
+        .length = proc_count * POINTER_SIZE,
+        .data = tag_proc_info,
+    };
+    emit_data_entry(module, &proc_table_data);
+
+    fori (i, 0, proc_count) {
+        DatumPatchInfo patch;
+        patch.kind = Datum_Patch_Data;
+        patch.data_id = proc_info_data.id;
+        patch.offset = tag_proc_info[i];
+        patch.index = proc_table_data.id;
+        patch.location = i * POINTER_SIZE;
+        bh_arr_push(module->data_patches, patch);
+    }
+
+    Tagged_Procedure_Type* tmp_data = bh_alloc(global_heap_allocator, 2 * POINTER_SIZE);
+    tmp_data[0] = 0;
+    tmp_data[1] = proc_count;
+    WasmDatum proc_table_global_data = {
+        .alignment = POINTER_SIZE,
+        .length = 2 * POINTER_SIZE,
+        .data = tmp_data,
+    };
+    emit_data_entry(module, &proc_table_global_data);
+
+    {
+        DatumPatchInfo patch;
+        patch.kind = Datum_Patch_Data;
+        patch.offset = 0;
+        patch.data_id = proc_table_data.id;
+        patch.index = proc_table_global_data.id;
+        patch.location = 0;
+        bh_arr_push(module->data_patches, patch);
+    }
+
+    return proc_table_global_data.id;
+
+#undef WRITE_SLICE
+#undef WRITE_PTR
+#undef PATCH
+}
+
diff --git a/include/astnodes.h b/include/astnodes.h
deleted file mode 100644 (file)
index 0052d23..0000000
+++ /dev/null
@@ -1,1841 +0,0 @@
-#ifndef ONYXASTNODES_H
-#define ONYXASTNODES_H
-
-#include "stb_ds.h"
-#include "lex.h"
-#include "types.h"
-#include "errors.h"
-
-#define AST_NODES              \
-    NODE(Node)                 \
-    NODE(Typed)                \
-                               \
-    NODE(NamedValue)           \
-    NODE(BinaryOp)             \
-    NODE(UnaryOp)              \
-    NODE(NumLit)               \
-    NODE(StrLit)               \
-    NODE(Local)                \
-    NODE(Call)                 \
-    NODE(Argument)             \
-    NODE(AddressOf)            \
-    NODE(Dereference)          \
-    NODE(Subscript)            \
-    NODE(FieldAccess)          \
-    NODE(UnaryFieldAccess)     \
-    NODE(SizeOf)               \
-    NODE(AlignOf)              \
-    NODE(FileContents)         \
-    NODE(StructLiteral)        \
-    NODE(ArrayLiteral)         \
-    NODE(RangeLiteral)         \
-    NODE(Compound)             \
-    NODE(IfExpression)         \
-    NODE(DoBlock)              \
-                               \
-    NODE(DirectiveSolidify)    \
-    NODE(DirectiveError)       \
-    NODE(DirectiveAddOverload) \
-    NODE(DirectiveOperator)    \
-    NODE(DirectiveExport)      \
-    NODE(DirectiveDefined)     \
-    NODE(DirectiveInit)        \
-    NODE(DirectiveLibrary)     \
-    NODE(DirectiveRemove)      \
-                               \
-    NODE(Return)               \
-    NODE(Jump)                 \
-    NODE(Use)                  \
-                               \
-    NODE(Block)                \
-    NODE(IfWhile)              \
-    NODE(For)                  \
-    NODE(Defer)                \
-    NODE(SwitchCase)           \
-    NODE(Switch)               \
-                               \
-    NODE(Type)                 \
-    NODE(BasicType)            \
-    NODE(PointerType)          \
-    NODE(FunctionType)         \
-    NODE(ArrayType)            \
-    NODE(SliceType)            \
-    NODE(DynArrType)           \
-    NODE(VarArgType)           \
-    NODE(StructType)           \
-    NODE(StructMember)         \
-    NODE(PolyStructType)       \
-    NODE(PolyStructParam)      \
-    NODE(PolyCallType)         \
-    NODE(EnumType)             \
-    NODE(EnumValue)            \
-    NODE(TypeAlias)            \
-    NODE(TypeRawAlias)         \
-    NODE(CompoundType)         \
-    NODE(TypeOf)               \
-    NODE(DistinctType)         \
-                               \
-    NODE(Binding)              \
-    NODE(Alias)                \
-    NODE(Injection)            \
-    NODE(MemRes)               \
-    NODE(Include)              \
-    NODE(UsePackage)           \
-    NODE(Global)               \
-    NODE(Param)                \
-    NODE(Function)             \
-    NODE(OverloadedFunction)   \
-    NODE(Interface)            \
-    NODE(Constraint)           \
-                               \
-    NODE(PolyParam)            \
-    NODE(PolySolution)         \
-    NODE(SolidifiedFunction)   \
-    NODE(PolyProc)             \
-    NODE(PolyQuery)            \
-                               \
-    NODE(Note)                 \
-    NODE(CallSite)             \
-                               \
-    NODE(CodeBlock)            \
-    NODE(DirectiveInsert)      \
-    NODE(Macro)                \
-                               \
-    NODE(ForeignBlock)         \
-                               \
-    NODE(Package)              \
-                               \
-    NODE(ZeroValue)
-
-#define NODE(name) typedef struct Ast ## name Ast ## name;
-AST_NODES
-#undef NODE
-
-typedef struct Package Package;
-
-typedef struct Scope {
-    u64 id;
-    struct Scope *parent;
-    OnyxFilePos created_at;
-    char* name;
-    Table(AstNode *) symbols;
-} Scope;
-
-
-typedef enum AstKind {
-    Ast_Kind_Error,
-    Ast_Kind_Package,
-    Ast_Kind_Load_File,
-    Ast_Kind_Load_Path,
-    Ast_Kind_Load_All,
-    Ast_Kind_Library_Path,
-    Ast_Kind_Memres,
-
-    Ast_Kind_Binding,
-    Ast_Kind_Alias,
-    Ast_Kind_Injection,
-    Ast_Kind_Function,
-    Ast_Kind_Overloaded_Function,
-    Ast_Kind_Polymorphic_Proc,
-    Ast_Kind_Polymorph_Query,
-    Ast_Kind_Interface,
-    Ast_Kind_Constraint,
-    Ast_Kind_Constraint_Sentinel,
-    Ast_Kind_Block,
-    Ast_Kind_Local,
-    Ast_Kind_Global,
-    Ast_Kind_Symbol,
-
-    Ast_Kind_Unary_Op,
-    Ast_Kind_Binary_Op,
-
-    Ast_Kind_Compound,
-    Ast_Kind_Named_Value,
-
-    Ast_Kind_Type_Start,
-    Ast_Kind_Type,
-    Ast_Kind_Basic_Type,
-    Ast_Kind_Pointer_Type,
-    Ast_Kind_Function_Type,
-    Ast_Kind_Array_Type,
-    Ast_Kind_Slice_Type,
-    Ast_Kind_DynArr_Type,
-    Ast_Kind_VarArg_Type,
-    Ast_Kind_Struct_Type,
-    Ast_Kind_Poly_Struct_Type,
-    Ast_Kind_Poly_Call_Type,
-    Ast_Kind_Enum_Type,
-    Ast_Kind_Type_Alias,
-    Ast_Kind_Type_Raw_Alias,
-    Ast_Kind_Type_Compound,
-    Ast_Kind_Typeof,
-    Ast_Kind_Distinct_Type,
-    Ast_Kind_Type_End,
-
-    Ast_Kind_Struct_Member,
-    Ast_Kind_Enum_Value,
-
-    Ast_Kind_NumLit,
-    Ast_Kind_StrLit,
-    Ast_Kind_Param,
-    Ast_Kind_Argument,
-    Ast_Kind_Call,
-    Ast_Kind_Intrinsic_Call,
-    Ast_Kind_Return,
-    Ast_Kind_Address_Of,
-    Ast_Kind_Dereference,
-    Ast_Kind_Subscript,
-    Ast_Kind_Slice,
-    Ast_Kind_Field_Access,
-    Ast_Kind_Unary_Field_Access,
-    Ast_Kind_Pipe,
-    Ast_Kind_Method_Call,
-    Ast_Kind_Range_Literal,
-    Ast_Kind_Size_Of,
-    Ast_Kind_Align_Of,
-    Ast_Kind_File_Contents,
-    Ast_Kind_Struct_Literal,
-    Ast_Kind_Array_Literal,
-    Ast_Kind_If_Expression,
-
-    Ast_Kind_If,
-    Ast_Kind_For,
-    Ast_Kind_While,
-    Ast_Kind_Jump,
-    Ast_Kind_Use,
-    Ast_Kind_Defer,
-    Ast_Kind_Switch,
-    Ast_Kind_Switch_Case,
-
-    Ast_Kind_Directive_Solidify,
-    Ast_Kind_Static_If,
-    Ast_Kind_Directive_Error,
-    Ast_Kind_Directive_Add_Overload,
-    Ast_Kind_Directive_Operator,
-    Ast_Kind_Directive_Export,
-    Ast_Kind_Directive_Defined,
-    Ast_Kind_Directive_Init,
-    Ast_Kind_Directive_Library,
-    Ast_Kind_Directive_Remove,
-    Ast_Kind_Call_Site,
-
-    Ast_Kind_Code_Block,
-    Ast_Kind_Directive_Insert,
-    Ast_Kind_Macro,
-    Ast_Kind_Do_Block,
-
-    Ast_Kind_Foreign_Block,
-
-    Ast_Kind_Zero_Value,
-
-    Ast_Kind_Note,
-
-    Ast_Kind_Count
-} AstKind;
-
-// NOTE: Some of these flags will overlap since there are
-// only 32-bits of flags to play with
-typedef enum AstFlags {
-    // Top-level flags
-    Ast_Flag_Const                 = BH_BIT(1),
-    Ast_Flag_Comptime              = BH_BIT(2),
-    Ast_Flag_Private_Package       = BH_BIT(3),
-    Ast_Flag_Private_File          = BH_BIT(4),
-
-    // Function flags
-    Ast_Flag_Function_Used         = BH_BIT(5),
-
-    // Expression flags
-    Ast_Flag_Expr_Ignored          = BH_BIT(6),
-    Ast_Flag_Address_Taken         = BH_BIT(7),
-
-    // Type flags
-    Ast_Flag_Type_Is_Resolved      = BH_BIT(8),
-
-    Ast_Flag_No_Clone              = BH_BIT(9),
-
-    Ast_Flag_Cannot_Take_Addr      = BH_BIT(10),
-
-    // HACK: NullProcHack
-    Ast_Flag_Proc_Is_Null          = BH_BIT(11),
-
-    Ast_Flag_From_Polymorphism     = BH_BIT(12),
-
-    Ast_Flag_Incomplete_Body       = BH_BIT(13),
-
-    Ast_Flag_Array_Literal_Typed   = BH_BIT(14),
-
-    Ast_Flag_Has_Been_Symres       = BH_BIT(15),
-
-    Ast_Flag_Has_Been_Checked      = BH_BIT(16),
-
-    Ast_Flag_Static_If_Resolved    = BH_BIT(17),
-
-    Ast_Flag_Symbol_Invisible      = BH_BIT(18),
-
-    Ast_Flag_Header_Check_No_Error = BH_BIT(19),
-
-    Ast_Flag_Decl_Followed_By_Init = BH_BIT(20),
-
-    Ast_Flag_Param_Symbol_Dirty    = BH_BIT(21),
-
-    Ast_Flag_Dead                  = BH_BIT(22),
-} AstFlags;
-
-typedef enum UnaryOp {
-    Unary_Op_Negate,
-    Unary_Op_Not,
-    Unary_Op_Bitwise_Not,
-    Unary_Op_Cast,
-    Unary_Op_Auto_Cast,
-} UnaryOp;
-
-typedef enum BinaryOp {
-    Binary_Op_Add              = 0,
-    Binary_Op_Minus            = 1,
-    Binary_Op_Multiply         = 2,
-    Binary_Op_Divide           = 3,
-    Binary_Op_Modulus          = 4,
-
-    Binary_Op_Equal            = 5,
-    Binary_Op_Not_Equal        = 6,
-    Binary_Op_Less             = 7,
-    Binary_Op_Less_Equal       = 8,
-    Binary_Op_Greater          = 9,
-    Binary_Op_Greater_Equal    = 10,
-
-    Binary_Op_And              = 11,
-    Binary_Op_Or               = 12,
-    Binary_Op_Xor              = 13,
-    Binary_Op_Shl              = 14,
-    Binary_Op_Shr              = 15,
-    Binary_Op_Sar              = 16,
-
-    Binary_Op_Bool_And         = 17,
-    Binary_Op_Bool_Or          = 18,
-
-    Binary_Op_Assign_Start     = 19,
-    Binary_Op_Assign           = 20,
-    Binary_Op_Assign_Add       = 21,
-    Binary_Op_Assign_Minus     = 22,
-    Binary_Op_Assign_Multiply  = 23,
-    Binary_Op_Assign_Divide    = 24,
-    Binary_Op_Assign_Modulus   = 25,
-    Binary_Op_Assign_And       = 26,
-    Binary_Op_Assign_Or        = 27,
-    Binary_Op_Assign_Xor       = 28,
-    Binary_Op_Assign_Shl       = 29,
-    Binary_Op_Assign_Shr       = 30,
-    Binary_Op_Assign_Sar       = 31,
-    Binary_Op_Assign_End       = 32,
-
-    Binary_Op_Pipe             = 33,
-    Binary_Op_Range            = 34,
-    Binary_Op_Method_Call      = 35,
-
-    Binary_Op_Subscript        = 36,
-    Binary_Op_Subscript_Equals = 37,
-    Binary_Op_Ptr_Subscript    = 38,
-
-    Binary_Op_Count
-} BinaryOp;
-
-extern const char *binaryop_string[Binary_Op_Count];
-
-typedef enum OnyxIntrinsic {
-    ONYX_INTRINSIC_UNDEFINED,
-
-    ONYX_INTRINSIC_MEMORY_SIZE, ONYX_INTRINSIC_MEMORY_GROW,
-    ONYX_INTRINSIC_MEMORY_COPY, ONYX_INTRINSIC_MEMORY_FILL,
-
-    ONYX_INTRINSIC_INITIALIZE,
-
-    ONYX_INTRINSIC_I32_CLZ,   ONYX_INTRINSIC_I32_CTZ, ONYX_INTRINSIC_I32_POPCNT,
-    ONYX_INTRINSIC_I32_AND,   ONYX_INTRINSIC_I32_OR,  ONYX_INTRINSIC_I32_XOR,
-    ONYX_INTRINSIC_I32_SHL,   ONYX_INTRINSIC_I32_SLR, ONYX_INTRINSIC_I32_SAR,
-    ONYX_INTRINSIC_I32_ROTL,  ONYX_INTRINSIC_I32_ROTR,
-
-    ONYX_INTRINSIC_I64_CLZ,   ONYX_INTRINSIC_I64_CTZ, ONYX_INTRINSIC_I64_POPCNT,
-    ONYX_INTRINSIC_I64_AND,   ONYX_INTRINSIC_I64_OR,  ONYX_INTRINSIC_I64_XOR,
-    ONYX_INTRINSIC_I64_SHL,   ONYX_INTRINSIC_I64_SLR, ONYX_INTRINSIC_I64_SAR,
-    ONYX_INTRINSIC_I64_ROTL,  ONYX_INTRINSIC_I64_ROTR,
-
-    ONYX_INTRINSIC_F32_ABS,   ONYX_INTRINSIC_F32_SQRT,
-    ONYX_INTRINSIC_F32_CEIL,  ONYX_INTRINSIC_F32_FLOOR,
-    ONYX_INTRINSIC_F32_TRUNC, ONYX_INTRINSIC_F32_NEAREST,
-    ONYX_INTRINSIC_F32_MIN,   ONYX_INTRINSIC_F32_MAX,
-    ONYX_INTRINSIC_F32_COPYSIGN,
-
-    ONYX_INTRINSIC_F64_ABS,   ONYX_INTRINSIC_F64_SQRT,
-    ONYX_INTRINSIC_F64_CEIL,  ONYX_INTRINSIC_F64_FLOOR,
-    ONYX_INTRINSIC_F64_TRUNC, ONYX_INTRINSIC_F64_NEAREST,
-    ONYX_INTRINSIC_F64_MIN,   ONYX_INTRINSIC_F64_MAX,
-    ONYX_INTRINSIC_F64_COPYSIGN,
-
-
-
-    ONYX_INTRINSIC_V128_CONST,
-    ONYX_INTRINSIC_I8X16_CONST, ONYX_INTRINSIC_I16X8_CONST,
-    ONYX_INTRINSIC_I32X4_CONST, ONYX_INTRINSIC_I64X2_CONST,
-    ONYX_INTRINSIC_F32X4_CONST, ONYX_INTRINSIC_F64X2_CONST,
-    ONYX_INTRINSIC_I8X16_SHUFFLE,
-
-    ONYX_INTRINSIC_I8X16_EXTRACT_LANE_S, ONYX_INTRINSIC_I8X16_EXTRACT_LANE_U, ONYX_INTRINSIC_I8X16_REPLACE_LANE,
-    ONYX_INTRINSIC_I16X8_EXTRACT_LANE_S, ONYX_INTRINSIC_I16X8_EXTRACT_LANE_U, ONYX_INTRINSIC_I16X8_REPLACE_LANE,
-    ONYX_INTRINSIC_I32X4_EXTRACT_LANE, ONYX_INTRINSIC_I32X4_REPLACE_LANE,
-    ONYX_INTRINSIC_I64X2_EXTRACT_LANE, ONYX_INTRINSIC_I64X2_REPLACE_LANE,
-    ONYX_INTRINSIC_F32X4_EXTRACT_LANE, ONYX_INTRINSIC_F32X4_REPLACE_LANE,
-    ONYX_INTRINSIC_F64X2_EXTRACT_LANE, ONYX_INTRINSIC_F64X2_REPLACE_LANE,
-
-    ONYX_INTRINSIC_I8X16_SWIZZLE,
-    ONYX_INTRINSIC_I8X16_SPLAT, ONYX_INTRINSIC_I16X8_SPLAT,
-    ONYX_INTRINSIC_I32X4_SPLAT, ONYX_INTRINSIC_I64X2_SPLAT,
-    ONYX_INTRINSIC_F32X4_SPLAT, ONYX_INTRINSIC_F64X2_SPLAT,
-
-    ONYX_INTRINSIC_I8X16_EQ, ONYX_INTRINSIC_I8X16_NEQ,
-    ONYX_INTRINSIC_I8X16_LT_S, ONYX_INTRINSIC_I8X16_LT_U,
-    ONYX_INTRINSIC_I8X16_GT_S, ONYX_INTRINSIC_I8X16_GT_U,
-    ONYX_INTRINSIC_I8X16_LE_S, ONYX_INTRINSIC_I8X16_LE_U,
-    ONYX_INTRINSIC_I8X16_GE_S, ONYX_INTRINSIC_I8X16_GE_U,
-
-    ONYX_INTRINSIC_I16X8_EQ, ONYX_INTRINSIC_I16X8_NEQ,
-    ONYX_INTRINSIC_I16X8_LT_S, ONYX_INTRINSIC_I16X8_LT_U,
-    ONYX_INTRINSIC_I16X8_GT_S, ONYX_INTRINSIC_I16X8_GT_U,
-    ONYX_INTRINSIC_I16X8_LE_S, ONYX_INTRINSIC_I16X8_LE_U,
-    ONYX_INTRINSIC_I16X8_GE_S, ONYX_INTRINSIC_I16X8_GE_U,
-
-    ONYX_INTRINSIC_I32X4_EQ, ONYX_INTRINSIC_I32X4_NEQ,
-    ONYX_INTRINSIC_I32X4_LT_S, ONYX_INTRINSIC_I32X4_LT_U,
-    ONYX_INTRINSIC_I32X4_GT_S, ONYX_INTRINSIC_I32X4_GT_U,
-    ONYX_INTRINSIC_I32X4_LE_S, ONYX_INTRINSIC_I32X4_LE_U,
-    ONYX_INTRINSIC_I32X4_GE_S, ONYX_INTRINSIC_I32X4_GE_U,
-
-    ONYX_INTRINSIC_F32X4_EQ, ONYX_INTRINSIC_F32X4_NEQ,
-    ONYX_INTRINSIC_F32X4_LT, ONYX_INTRINSIC_F32X4_GT,
-    ONYX_INTRINSIC_F32X4_LE, ONYX_INTRINSIC_F32X4_GE,
-
-    ONYX_INTRINSIC_F64X2_EQ, ONYX_INTRINSIC_F64X2_NEQ,
-    ONYX_INTRINSIC_F64X2_LT, ONYX_INTRINSIC_F64X2_GT,
-    ONYX_INTRINSIC_F64X2_LE, ONYX_INTRINSIC_F64X2_GE,
-
-    ONYX_INTRINSIC_V128_NOT, ONYX_INTRINSIC_V128_AND, ONYX_INTRINSIC_V128_ANDNOT,
-    ONYX_INTRINSIC_V128_OR, ONYX_INTRINSIC_V128_XOR, ONYX_INTRINSIC_V128_BITSELECT,
-
-    ONYX_INTRINSIC_I8X16_ABS, ONYX_INTRINSIC_I8X16_NEG,
-    ONYX_INTRINSIC_I8X16_ANY_TRUE, ONYX_INTRINSIC_I8X16_ALL_TRUE,
-    ONYX_INTRINSIC_I8X16_BITMASK,
-    ONYX_INTRINSIC_I8X16_NARROW_I16X8_S, ONYX_INTRINSIC_I8X16_NARROW_I16X8_U,
-    ONYX_INTRINSIC_I8X16_SHL, ONYX_INTRINSIC_I8X16_SHR_S, ONYX_INTRINSIC_I8X16_SHR_U,
-    ONYX_INTRINSIC_I8X16_ADD, ONYX_INTRINSIC_I8X16_ADD_SAT_S, ONYX_INTRINSIC_I8X16_ADD_SAT_U,
-    ONYX_INTRINSIC_I8X16_SUB, ONYX_INTRINSIC_I8X16_SUB_SAT_S, ONYX_INTRINSIC_I8X16_SUB_SAT_U,
-    ONYX_INTRINSIC_I8X16_MIN_S, ONYX_INTRINSIC_I8X16_MIN_U,
-    ONYX_INTRINSIC_I8X16_MAX_S, ONYX_INTRINSIC_I8X16_MAX_U,
-    ONYX_INTRINSIC_I8X16_AVGR_U,
-
-    ONYX_INTRINSIC_I16X8_ABS, ONYX_INTRINSIC_I16X8_NEG,
-    ONYX_INTRINSIC_I16X8_ANY_TRUE, ONYX_INTRINSIC_I16X8_ALL_TRUE,
-    ONYX_INTRINSIC_I16X8_BITMASK,
-    ONYX_INTRINSIC_I16X8_NARROW_I32X4_S, ONYX_INTRINSIC_I16X8_NARROW_I32X4_U,
-    ONYX_INTRINSIC_I16X8_WIDEN_LOW_I8X16_S, ONYX_INTRINSIC_I16X8_WIDEN_HIGH_I8X16_S,
-    ONYX_INTRINSIC_I16X8_WIDEN_LOW_I8X16_U, ONYX_INTRINSIC_I16X8_WIDEN_HIGH_I8X16_U,
-    ONYX_INTRINSIC_I16X8_SHL, ONYX_INTRINSIC_I16X8_SHR_S, ONYX_INTRINSIC_I16X8_SHR_U,
-    ONYX_INTRINSIC_I16X8_ADD, ONYX_INTRINSIC_I16X8_ADD_SAT_S, ONYX_INTRINSIC_I16X8_ADD_SAT_U,
-    ONYX_INTRINSIC_I16X8_SUB, ONYX_INTRINSIC_I16X8_SUB_SAT_S, ONYX_INTRINSIC_I16X8_SUB_SAT_U,
-    ONYX_INTRINSIC_I16X8_MUL,
-    ONYX_INTRINSIC_I16X8_MIN_S, ONYX_INTRINSIC_I16X8_MIN_U,
-    ONYX_INTRINSIC_I16X8_MAX_S, ONYX_INTRINSIC_I16X8_MAX_U,
-    ONYX_INTRINSIC_I16X8_AVGR_U,
-
-    ONYX_INTRINSIC_I32X4_ABS, ONYX_INTRINSIC_I32X4_NEG,
-    ONYX_INTRINSIC_I32X4_ANY_TRUE, ONYX_INTRINSIC_I32X4_ALL_TRUE,
-    ONYX_INTRINSIC_I32X4_BITMASK,
-    ONYX_INTRINSIC_I32X4_WIDEN_LOW_I16X8_S, ONYX_INTRINSIC_I32X4_WIDEN_HIGH_I16X8_S,
-    ONYX_INTRINSIC_I32X4_WIDEN_LOW_I16X8_U, ONYX_INTRINSIC_I32X4_WIDEN_HIGH_I16X8_U,
-    ONYX_INTRINSIC_I32X4_SHL, ONYX_INTRINSIC_I32X4_SHR_S, ONYX_INTRINSIC_I32X4_SHR_U,
-    ONYX_INTRINSIC_I32X4_ADD, ONYX_INTRINSIC_I32X4_SUB, ONYX_INTRINSIC_I32X4_MUL,
-    ONYX_INTRINSIC_I32X4_MIN_S, ONYX_INTRINSIC_I32X4_MIN_U,
-    ONYX_INTRINSIC_I32X4_MAX_S, ONYX_INTRINSIC_I32X4_MAX_U,
-
-    ONYX_INTRINSIC_I64X2_NEG, ONYX_INTRINSIC_I64X2_SHL,
-    ONYX_INTRINSIC_I64X2_SHR_S, ONYX_INTRINSIC_I64X2_SHR_U,
-    ONYX_INTRINSIC_I64X2_ADD, ONYX_INTRINSIC_I64X2_SUB, ONYX_INTRINSIC_I64X2_MUL,
-
-    ONYX_INTRINSIC_F32X4_ABS, ONYX_INTRINSIC_F32X4_NEG, ONYX_INTRINSIC_F32X4_SQRT,
-    ONYX_INTRINSIC_F32X4_ADD, ONYX_INTRINSIC_F32X4_SUB,
-    ONYX_INTRINSIC_F32X4_MUL, ONYX_INTRINSIC_F32X4_DIV,
-    ONYX_INTRINSIC_F32X4_MIN, ONYX_INTRINSIC_F32X4_MAX,
-
-    ONYX_INTRINSIC_F64X2_ABS, ONYX_INTRINSIC_F64X2_NEG, ONYX_INTRINSIC_F64X2_SQRT,
-    ONYX_INTRINSIC_F64X2_ADD, ONYX_INTRINSIC_F64X2_SUB,
-    ONYX_INTRINSIC_F64X2_MUL, ONYX_INTRINSIC_F64X2_DIV,
-    ONYX_INTRINSIC_F64X2_MIN, ONYX_INTRINSIC_F64X2_MAX,
-
-    ONYX_INTRINSIC_I32X4_TRUNC_SAT_F32X4_S,
-    ONYX_INTRINSIC_I32X4_TRUNC_SAT_F32X4_U,
-    ONYX_INTRINSIC_F32X4_CONVERT_I32X4_S,
-    ONYX_INTRINSIC_F32X4_CONVERT_I32X4_U,
-
-    ONYX_INTRINSIC_ATOMIC_WAIT,
-    ONYX_INTRINSIC_ATOMIC_NOTIFY,
-
-    ONYX_INTRINSIC_ATOMIC_FENCE,
-
-    ONYX_INTRINSIC_ATOMIC_LOAD,
-    ONYX_INTRINSIC_ATOMIC_STORE,
-    ONYX_INTRINSIC_ATOMIC_ADD,
-    ONYX_INTRINSIC_ATOMIC_SUB,
-    ONYX_INTRINSIC_ATOMIC_AND,
-    ONYX_INTRINSIC_ATOMIC_OR,
-    ONYX_INTRINSIC_ATOMIC_XOR,
-    ONYX_INTRINSIC_ATOMIC_XCHG,
-    ONYX_INTRINSIC_ATOMIC_CMPXCHG,
-} OnyxIntrinsic;
-
-typedef enum CallingConvention {
-    CC_Undefined,
-    CC_Return_Wasm,
-    CC_Return_Stack
-} CallingConvention;
-
-typedef enum JumpType {
-    Jump_Type_Break,
-    Jump_Type_Continue,
-    Jump_Type_Fallthrough,
-    Jump_Type_Return,
-
-    Jump_Type_Count,
-} JumpType;
-
-typedef enum ForLoopType {
-    For_Loop_Invalid,
-    For_Loop_Range,
-    For_Loop_Array,
-    For_Loop_Slice,
-    For_Loop_DynArr,
-    For_Loop_Iterator,
-} ForLoopType;
-
-typedef enum ParamPassType {
-    Param_Pass_Invalid,
-    Param_Pass_By_Value,
-    Param_Pass_By_VarArg,
-    Param_Pass_By_Implicit_Pointer,
-} ParamPassType;
-
-typedef enum VarArgKind {
-    VA_Kind_Not_VA,
-    VA_Kind_Typed,
-    VA_Kind_Any,
-    VA_Kind_Untyped,
-} VarArgKind;
-
-typedef enum BlockRule {
-    Block_Rule_New_Scope         = BH_BIT(1),
-    Block_Rule_Emit_Instructions = BH_BIT(2),
-    Block_Rule_Clear_Defer       = BH_BIT(3),
-    Block_Rule_Override_Return   = BH_BIT(4),
-
-    Block_Rule_Normal     = Block_Rule_New_Scope | Block_Rule_Clear_Defer | Block_Rule_Emit_Instructions,
-    Block_Rule_Macro      = Block_Rule_New_Scope,
-    Block_Rule_Code_Block = Block_Rule_New_Scope,
-    Block_Rule_Do_Block   = Block_Rule_New_Scope | Block_Rule_Clear_Defer | Block_Rule_Override_Return | Block_Rule_Emit_Instructions,
-} BlockRule;
-
-typedef enum ConstraintCheckStatus {
-    Constraint_Check_Status_Queued,
-    Constraint_Check_Status_Failed,
-    Constraint_Check_Status_Success,
-} ConstraintCheckStatus;
-
-typedef struct ConstraintContext {
-    bh_arr(AstConstraint *) constraints;
-    ConstraintCheckStatus *constraint_checks;
-    b32 constraints_met : 1;
-    b32 produce_errors  : 1;
-} ConstraintContext;
-
-
-typedef struct Arguments Arguments;
-struct Arguments {
-    bh_arr(AstTyped *) values;
-    bh_arr(AstNamedValue *) named_values;
-
-    // How many arguments were not baked.
-    i32 used_argument_count;
-};
-
-
-// Base Nodes
-#define AstNode_base \
-    AstKind kind;             \
-    u32 flags;                \
-    OnyxToken *token;         \
-    struct Entity* entity;    \
-    AstNode *next
-struct AstNode { AstNode_base; };
-
-// NOTE: 'type_node' is filled out by the parser.
-// For a type such as '^^i32', the tree would look something like
-//
-//      Typed Thing -> AstPointerType -> AstPointerType -> AstNode (symbol node)
-//
-// The symbol node will be filled out during symbol resolution.
-// It will end up pointing to an AstBasicType that corresponds to
-// the underlying type.
-//
-// 'type' is filled out afterwards. If it is NULL, the Type* is built
-// using the type_node. This can then be used to typecheck this node.
-#define AstTyped_base       \
-    AstNode_base;           \
-    AstType *type_node;     \
-    Type *type
-struct AstTyped { AstTyped_base; };
-
-// Expression Nodes
-struct AstNamedValue    { AstTyped_base; AstTyped* value; };
-struct AstUnaryOp       { AstTyped_base; UnaryOp operation; AstTyped *expr; };
-struct AstStrLit        { AstTyped_base; u64 data_id; u64 length; b32 is_cstr: 1; };
-struct AstLocal         { AstTyped_base; };
-struct AstDereference   { AstTyped_base; AstTyped *expr; };
-struct AstSizeOf        { AstTyped_base; AstType *so_ast_type; Type *so_type; u64 size; };
-struct AstAlignOf       { AstTyped_base; AstType *ao_ast_type; Type *ao_type; u64 alignment; };
-struct AstNumLit        {
-    AstTyped_base;
-    union {
-        i32 i;
-        i64 l;
-        f32 f;
-        f64 d;
-    } value;
-
-    b32 was_hex_literal : 1;
-};
-struct AstBinaryOp      {
-    AstTyped_base;
-    BinaryOp operation;
-    AstTyped *left, *right;
-
-    Arguments *overload_args; // This is set of the binary operator is attempted to be overloaded
-                              // but isnt successful yet.
-    AstBinaryOp *potential_substitute;
-};
-struct AstAddressOf     {
-    AstTyped_base;
-    AstTyped *expr;
-
-    AstBinaryOp *potential_substitute;
-
-    // This is set by check_method_call.
-    // If set, the address of node can be removed if the
-    // type checking does not pass for it. This makes it
-    // possible to have something that will optionally
-    // have its address taken, if necessary.
-    b32 can_be_removed : 1;
-};
-struct AstArgument      {
-    AstTyped_base;
-
-    AstTyped *value;
-
-    VarArgKind va_kind;
-    b32 is_baked : 1;
-    b32 pass_as_any : 1;
-};
-struct AstSubscript   {
-    AstTyped_base;
-    BinaryOp __unused_operation; // This will be set to Binary_Op_Subscript
-    AstTyped *addr;
-    AstTyped *expr;
-
-    Arguments *overload_args; // This is set of the binary operator is attempted to be overloaded
-                              // but isnt successful yet.
-    AstBinaryOp *potential_substitute;
-
-    u64 elem_size;
-};
-struct AstFieldAccess   {
-    AstTyped_base;
-    AstTyped *expr;
-    u32 offset;
-    u32 idx;
-    char* field; // If token is null, defer to field
-};
-struct AstFileContents  {
-    AstTyped_base;
-
-    AstTyped *filename_expr;
-    char *filename; // The parsed file name, with '\' sequences removed and resolved to a particular file if possible.
-
-    u32 data_id, size;
-};
-struct AstUnaryFieldAccess {
-    AstTyped_base;
-
-    // "token" represents the field. This does not need an "offset" or an "index"
-    // because this node is meant to be replaced.
-};
-struct AstStructLiteral {
-    AstTyped_base;
-
-    AstTyped *stnode;
-
-    Arguments args;
-};
-struct AstArrayLiteral {
-    AstTyped_base;
-
-    AstTyped *atnode;
-
-    bh_arr(AstTyped *) values;
-};
-struct AstRangeLiteral {
-    AstTyped_base; 
-
-    // HACK: Currently, range literals are parsed as binary operators, which means
-    // the first sizeof(AstBinaryOp) bytes of this structure must match that of
-    // AstBinaryOp, which means I need this dummy field here.
-    //                                              - brendanfh 2020/12/23
-    BinaryOp __unused_operation;
-    AstTyped *low, *high;
-
-    // Currently, there is no way to specify this in the grammar, but it is set
-    // to be the initial value of the `step` member of the range structure in
-    // core/builtin.onyx.
-    //                                              - brendanfh 2020/12/23
-    AstTyped *step;
-};
-struct AstCall {
-    AstTyped_base;
-
-    Arguments args;
-
-    union {
-        AstTyped *callee;
-        OnyxIntrinsic intrinsic;
-    };
-
-    VarArgKind va_kind;
-};
-struct AstCompound {
-    AstTyped_base;
-
-    bh_arr(AstTyped *) exprs;
-};
-struct AstIfExpression {
-    AstTyped_base;
-
-    AstTyped* cond;
-    AstTyped* true_expr;
-    AstTyped* false_expr;
-};
-struct AstDoBlock {
-    AstTyped_base;
-
-    AstBlock* block;
-};
-struct AstZeroValue {
-    AstTyped_base;
-};
-
-struct AstDirectiveSolidify {
-    AstTyped_base;
-
-    AstFunction* poly_proc;
-    bh_arr(AstPolySolution) known_polyvars;
-
-    AstNode* resolved_proc;
-};
-
-// Intruction Node
-struct AstReturn        { AstNode_base; AstTyped* expr; };
-struct AstJump          { AstNode_base; JumpType jump; u32 count; };
-
-typedef struct QualifiedUse {
-    OnyxToken* symbol_name;
-    OnyxToken* as_name;
-} QualifiedUse;
-struct AstUse           {
-    AstNode_base;
-
-    AstTyped* expr;
-    bh_arr(QualifiedUse) only;
-};
-
-// Structure Nodes
-struct AstBlock         {
-    AstNode_base;
-
-    AstNode *body;
-
-    Scope *scope;
-    Scope *binding_scope;
-    BlockRule rules;
-
-    u32 statement_idx;
-};
-struct AstDefer         { AstNode_base; AstNode *stmt; };
-struct AstFor           {
-    AstNode_base;
-
-    // NOTE: Stores the iteration variable
-    Scope *scope;
-
-    // NOTE: Local defining the iteration variable
-    AstLocal* var;
-
-    // NOTE: This can be any expression, but it is checked that
-    // it is of a type that we know how to iterate over.
-    AstTyped* iter;
-    ForLoopType loop_type;
-
-    AstBlock *stmt;
-
-    // ROBUSTNESS: This should be able to be set by a compile time variable at some point.
-    // But for now, this will do.
-    b32 by_pointer : 1;
-    b32 no_close   : 1;
-};
-struct AstIfWhile {
-    AstNode_base;
-
-    Scope *scope;
-    AstNode* initialization;
-
-    AstTyped *cond;
-
-    AstBlock *true_stmt;
-    AstBlock *false_stmt;
-
-    union {
-        // Used by Static_If
-        struct {
-            Scope *defined_in_scope;
-            bh_arr(struct Entity *) true_entities;
-            bh_arr(struct Entity *) false_entities;
-        };
-
-        // Used by While
-        b32 bottom_test;
-    };
-};
-typedef struct AstIfWhile AstIf;
-typedef struct AstIfWhile AstWhile;
-
-typedef enum SwitchKind {
-    Switch_Kind_Integer,
-    Switch_Kind_Use_Equals,
-} SwitchKind;
-
-typedef struct CaseToBlock {
-    AstTyped *original_value;
-    AstBinaryOp *comparison;
-    AstBlock *block;
-} CaseToBlock;
-
-struct AstSwitchCase {
-    AstNode_base;
-
-    // NOTE: All expressions that end up in this block
-    bh_arr(AstTyped *) values;
-    
-    AstBlock *block;
-
-    b32 is_default: 1; // Could this be inferred by the values array being null?
-};
-
-struct AstSwitch {
-    AstNode_base;
-
-    Scope *scope;
-    AstNode* initialization;
-
-    AstTyped *expr;
-
-    AstBlock *case_block;
-
-    bh_arr(AstSwitchCase *) cases;
-    AstBlock *default_case;
-
-    i32 yield_return_index;
-    SwitchKind switch_kind;
-
-    union {
-        struct {
-            // NOTE: This is a mapping from the compile time known case value
-            // to a pointer to the block that it is associated with.
-            bh_imap case_map;
-            u64 min_case, max_case;
-        };
-
-        struct {
-            // NOTE: This is a mapping from the '==' binary op node to
-            // a pointer to the block that it is associated with.
-            bh_arr(CaseToBlock) case_exprs;
-        };
-    };
-};
-
-// Type Nodes
-// NOTE: This node is very similar to an AstNode, just
-// without the 'next' member. This is because types
-// can't be in expressions so a 'next' thing
-// doesn't make sense.
-#define AstType_base       \
-    AstKind kind;          \
-    u32 flags;             \
-    OnyxToken* token;      \
-    struct Entity* entity; \
-    void* __unused;        \
-    u64 type_id;           \
-    Type* type
-struct AstType { AstType_base; };
-
-struct AstBasicType     { AstType_base; Type* basic_type; };
-struct AstPointerType   { AstType_base; AstType* elem; };
-struct AstArrayType     { AstType_base; AstType* elem; AstTyped *count_expr; };
-struct AstSliceType     { AstType_base; AstType* elem; };
-struct AstDynArrType    { AstType_base; AstType* elem; };
-struct AstVarArgType    { AstType_base; AstType* elem; };
-struct AstFunctionType  {
-    AstType_base;
-
-    // Used in a rare case in solve_for_polymorphic_param_type.
-    Type *partial_function_type;
-
-    AstType* return_type;
-
-    u64 param_count;
-    AstType* params[];
-};
-struct AstStructType {
-    AstType_base;
-    char *name;
-
-    bh_arr(AstStructMember *) members;
-    bh_arr(AstTyped *) meta_tags;
-
-    // u32 min_alignment, min_size;
-    AstTyped *min_alignment_, *min_size_;
-
-    // NOTE: Used to cache the actual type, since building
-    // a struct type is kind of complicated and should
-    // only happen once.
-    Type *stcache;
-
-    // NOTE: This type is used when the structure has not been
-    // completely generated, but is a valid pointer to where the
-    // type will be generated to.
-    Type *pending_type;
-    
-    // NOTE: Used to store statically bound expressions in the struct.
-    Scope* scope;
-
-    struct Entity* entity_type;
-    struct Entity* entity_defaults;
-
-    OnyxFilePos polymorphic_error_loc;
-    ConstraintContext constraints;
-
-    bh_arr(AstType *)       polymorphic_argument_types;
-    bh_arr(AstPolySolution) polymorphic_arguments;
-
-    b32 pending_type_is_valid : 1;
-    b32 is_union              : 1;
-    b32 is_packed             : 1;
-    b32 ready_to_build_type   : 1;
-};
-struct AstStructMember {
-    AstTyped_base;
-    AstTyped* initial_value;
-
-    bh_arr(AstTyped *) meta_tags;
-
-    b32 is_used : 1;
-};
-struct AstPolyStructParam {
-    AstTyped_base;
-};
-struct AstPolyStructType {
-    AstType_base;
-    char *name;
-
-    Scope *scope;
-    bh_arr(AstPolyStructParam) poly_params;
-    Table(AstStructType *) concrete_structs;
-
-    AstStructType* base_struct;
-};
-struct AstPolyCallType {
-    AstType_base;
-
-    AstType* callee;
-
-    // NOTE: These nodes can be either AstTypes, or AstTyped expressions.
-    bh_arr(AstNode *) params;
-};
-struct AstEnumType {
-    AstType_base;
-    char *name;
-    Scope *scope;
-
-    AstType *backing;
-    Type    *backing_type;
-
-    bh_arr(AstEnumValue *) values;
-
-    // NOTE: Used to cache the actual type for the same reason as above.
-    Type *etcache;
-
-    b32 is_flags : 1;
-};
-struct AstEnumValue    { AstTyped_base; AstTyped* value; };
-struct AstTypeAlias    { AstType_base; AstType* to; };
-struct AstTypeRawAlias { AstType_base; Type* to; };
-struct AstCompoundType {
-    AstType_base;
-
-    bh_arr(AstType *) types;
-};
-struct AstTypeOf {
-    AstType_base;
-
-    AstTyped* expr;
-    Type* resolved_type;
-};
-struct AstDistinctType {
-    AstType_base;
-    char *name;
-    AstType *base_type;
-    Type *dtcache;
-};
-
-// Top level nodes
-struct AstBinding       { AstTyped_base; AstNode* node; };
-struct AstAlias         { AstTyped_base; AstTyped* alias; };
-struct AstInclude       { AstNode_base;  AstTyped* name_node; char* name; };
-struct AstInjection     {
-    AstTyped_base;
-    AstTyped* full_loc;
-    AstTyped* to_inject;
-    AstTyped* dest;
-    OnyxToken *symbol;
-};
-struct AstMemRes        {
-    AstTyped_base;
-    AstTyped *initial_value;
-
-    struct Entity *type_entity;
-
-    b32 threadlocal : 1;
-
-    // Set and used in the wasm emission.
-    u32 data_id;
-    u32 tls_offset;
-};
-struct AstGlobal        {
-    AstTyped_base;
-
-    char* name;
-
-    OnyxToken* foreign_module;
-    OnyxToken* foreign_name;
-};
-struct AstParam {
-    // HACK CLEANUP: This does not need to have a local buried inside of it.
-    // Convert this to be AstTyped_base and pull the ParamPassType from AstLocal
-    // to here.                                        - brendanfh 2020/09/18
-    AstLocal *local;
-    AstTyped *default_value;
-
-    VarArgKind vararg_kind;
-    b32 is_used       : 1;
-    b32 use_processed : 1;
-    b32 is_baked      : 1;
-};
-typedef struct OverloadOption OverloadOption;
-struct OverloadOption {
-    // This is u64 because padding will make it that anyway.
-    // Consider: would there be any practical benefit to having the precedence setting
-    // be a compile-time known value? as opposed to a hardcoded value?
-    u64 precedence;
-
-    AstTyped* option;
-};
-
-struct AstOverloadedFunction {
-    AstTyped_base;
-
-    bh_arr(OverloadOption) overloads;
-
-    // CLEANUP: This is unused right now, but should be used to cache
-    // the complete set of overloads that can be used by an overloaded
-    // function.
-    bh_imap            all_overloads;
-
-    b32 locked : 1;
-    b32 only_local_functions : 1;
-};
-
-// @CLEANUP: Is this really necessary?
-typedef struct InterfaceParam {
-    OnyxToken *value_token;
-    OnyxToken *type_token;
-} InterfaceParam;
-
-typedef struct InterfaceConstraint {
-    AstTyped *expr;
-    AstType  *expected_type_expr;
-    Type     *expected_type;
-
-    b32 invert_condition: 1;
-} InterfaceConstraint;
-
-struct AstInterface {
-    AstTyped_base;
-    char *name;
-
-    bh_arr(InterfaceParam)      params;
-    bh_arr(InterfaceConstraint) exprs;
-};
-
-typedef enum ConstraintPhase {
-    Constraint_Phase_Waiting_To_Be_Queued = 0,
-    Constraint_Phase_Cloning_Expressions  = 1,
-    Constraint_Phase_Checking_Expressions = 2,
-    Constraint_Phase_Finished             = 3,
-} ConstraintPhase;
-
-struct AstConstraint {
-    AstNode_base;
-
-    ConstraintPhase phase;
-
-    AstInterface *interface;
-    bh_arr(AstType *) type_args;
-
-    ConstraintCheckStatus *report_status;
-
-    Scope* scope;
-    bh_arr(InterfaceConstraint) exprs;
-    u32 expr_idx;
-};
-
-
-struct AstPackage {
-    AstTyped_base;
-
-    // Allocated in the ast arena
-    char * package_name;
-
-    // NOTE: Symbol nodes
-    bh_arr(OnyxToken *) path;
-
-    Package* package;
-};
-
-//
-// Polymorphic procedures
-//
-
-typedef enum PolyParamKind {
-    // Dont love these names
-    PPK_Undefined,
-    PPK_Poly_Type,
-    PPK_Baked_Value,
-} PolyParamKind;
-
-typedef enum PolySolutionKind {
-    PSK_Undefined,
-    PSK_Type,
-    PSK_Value,
-} PolySolutionKind;
-
-typedef enum PolyProcLookupMethod {
-    PPLM_By_Arguments,
-    PPLM_By_Function_Type,
-} PolyProcLookupMethod;
-
-struct AstPolyParam {
-    PolyParamKind kind;
-
-    // The parameter index where the polymorphic variable occurs.
-    u32 idx;
-
-    // The symbol node that represents the polymorphic variable.
-    AstNode* poly_sym;
-
-    // The type expression that contains `poly_sym` in it somewhere.
-    // Matching polymorphic variables does a parallel tree traversal
-    // to find the pairing of the actual type and polymorphic variable
-    // symbol.
-    AstType* type_expr;
-
-    // Used for baked values. The expected type of the parameter.
-    Type* type;
-};
-
-struct AstPolySolution {
-    PolySolutionKind kind;
-    AstNode* poly_sym;
-
-    union {
-        struct {
-            // If `type` is null, it is filled in with this type.
-            AstType* ast_type;
-            Type*    type;
-        };
-
-        AstTyped* value;
-    };
-};
-
-struct AstSolidifiedFunction {
-    AstFunction   *func;
-    struct Entity *func_header_entity;
-};
-
-struct AstFunction {
-    AstTyped_base;
-
-    Scope *scope;
-
-    bh_arr(AstParam) params;
-    AstType* return_type;
-
-    AstBlock *body;
-
-    char* name;
-
-    // NOTE: This is NULL, unless this function was generated from a polymorphic
-    // procedure call. Then it is set to the token of the call node.
-    OnyxToken* generated_from;
-    Scope*     poly_scope;
-
-    // NOTE: This is NULL, unless this function is used in a "#export" directive.
-    // It is undefined which name it will have if there are multiple export directives
-    // for this particular function.
-    OnyxToken* exported_name;
-
-    OnyxToken* closing_brace;
-
-    union {
-        OnyxToken* intrinsic_name;
-
-        // NOTE: Used when the function is declared as foreign
-        struct {
-            OnyxToken* foreign_module;
-            OnyxToken* foreign_name;
-        };
-    };
-
-    struct Entity* entity_header;
-    struct Entity* entity_body;
-
-    ConstraintContext constraints;
-
-    bh_arr(AstTyped *) tags;
-
-    // Polymorphic procedures use the following fields
-    Scope *parent_scope_of_poly_proc;
-    bh_arr(AstPolyParam) poly_params;
-
-    bh_arr(AstPolySolution) known_slns;
-
-    Table(AstSolidifiedFunction) concrete_funcs;
-    bh_imap active_queries;
-
-    bh_arr(AstNode *) nodes_that_need_entities_after_clone;
-
-    b32 is_exported  : 1;
-    b32 is_foreign   : 1;
-    b32 is_intrinsic : 1;
-};
-
-struct AstPolyQuery {
-    AstNode_base;
-
-    AstFunction *proc;
-    PolyProcLookupMethod pp_lookup;
-    ptr given;
-    OnyxToken *error_loc;
-
-    bh_arr(AstPolySolution) slns;
-
-    AstFunction *function_header;
-
-    b32 error_on_fail : 1;     // Whether or not to report errors on failing to match.
-    b32 successful_symres : 1; // If something successful happened in symbol resolution
-};
-
-
-struct AstDirectiveError {
-    AstNode_base;
-
-    OnyxToken* error_msg;
-};
-
-struct AstDirectiveAddOverload {
-    AstNode_base;
-
-    // NOTE: used by the #add_overload directive. Initially set to a symbol,
-    // then resolved to an overloaded function.
-    AstNode *overloaded_function;
-
-    // See note in OverloadOption. This could be refactored into an OverloadOption?
-    u64 precedence;
-    AstTyped *overload;
-};
-
-struct AstDirectiveOperator {
-    AstNode_base;
-
-    BinaryOp operator;
-    AstTyped *overload;
-};
-
-struct AstDirectiveExport {
-    AstNode_base;
-
-    OnyxToken* export_name;
-    AstTyped* export_name_expr;
-    AstTyped* export;
-};
-
-struct AstDirectiveDefined {
-    AstTyped_base;
-    AstTyped *expr;
-
-    b32 is_defined: 1;
-};
-
-struct AstDirectiveRemove {
-    AstNode_base;
-};
-
-struct AstNote {
-    AstNode_base;
-};
-
-struct AstCallSite {
-    AstTyped_base;
-
-    OnyxToken* callsite_token;
-
-    AstStrLit* filename;
-    AstNumLit* line;
-    AstNumLit* column;
-};
-
-// Represents a "pastable" block of code.
-struct AstCodeBlock {
-    AstTyped_base;
-
-    AstNode *code;
-};
-
-struct AstDirectiveInsert {
-    AstTyped_base;
-
-    AstTyped *code_expr;
-};
-
-struct AstDirectiveInit {
-    AstNode_base;
-
-    AstTyped *init_proc;
-    bh_arr(AstDirectiveInit *) dependencies;
-};
-
-struct AstMacro {
-    AstTyped_base;
-
-    AstTyped* body;
-};
-
-struct AstDirectiveLibrary {
-    AstNode_base;
-
-    AstTyped *library_symbol; // This should resolve to a string literal
-    char *library_name;
-};
-
-struct AstForeignBlock {
-    AstTyped_base;
-
-    Scope* scope;
-    OnyxToken *module_name;
-    bh_arr(struct Entity *) captured_entities;
-
-    u32 foreign_block_number;
-};
-
-typedef enum EntityState {
-    Entity_State_Error,
-    
-    Entity_State_Parse_Builtin,
-    Entity_State_Introduce_Symbols,
-    Entity_State_Parse,
-    Entity_State_Resolve_Symbols,
-    Entity_State_Check_Types,
-    Entity_State_Code_Gen,
-    Entity_State_Finalized,
-    Entity_State_Failed,
-
-    Entity_State_Count,
-} EntityState;
-
-extern const char* entity_state_strings[Entity_State_Count];
-
-// NOTE: An Entity represents something will need to be
-// processed later down the pipeline.
-typedef enum EntityType {
-    Entity_Type_Unknown,
-
-    Entity_Type_Error,
-    Entity_Type_Note,
-    Entity_Type_Load_Path,
-    Entity_Type_Load_File,
-    Entity_Type_Binding,
-    Entity_Type_Use_Package,
-    Entity_Type_Static_If,
-    Entity_Type_String_Literal,
-    Entity_Type_File_Contents,
-    Entity_Type_Enum,
-    Entity_Type_Enum_Value,
-    Entity_Type_Type_Alias,
-    Entity_Type_Memory_Reservation_Type,
-    Entity_Type_Use,
-    Entity_Type_Interface,
-    Entity_Type_Constraint_Check,
-    Entity_Type_Polymorphic_Proc,
-    Entity_Type_Polymorph_Query,
-    Entity_Type_Macro,
-    Entity_Type_Foreign_Block,
-    Entity_Type_Foreign_Function_Header,
-    Entity_Type_Temp_Function_Header,    // Same as a Function_Header, except it disappears after it checks completely.
-    Entity_Type_Function_Header,
-    Entity_Type_Global_Header,
-    Entity_Type_Process_Directive,
-    Entity_Type_Struct_Member_Default,
-    Entity_Type_Memory_Reservation,
-    Entity_Type_Expression,
-    Entity_Type_Global,
-    Entity_Type_Overloaded_Function,
-    Entity_Type_Function,
-
-    Entity_Type_Count,
-} EntityType;
-
-extern const char* entity_type_strings[Entity_Type_Count];
-
-typedef struct Entity {
-    u32 id;
-
-    EntityType type;
-    EntityState state;
-
-    // TODO: Document this!
-    u32 macro_attempts;
-    u32 micro_attempts;
-
-    b32 entered_in_queue : 1;
-
-    Package *package;
-    Scope *scope;
-
-    // TODO: This is incomplete. Add proper cycle detection and halting.
-    // struct Entity *waiting_on;
-
-    union {
-        AstDirectiveError     *error;
-        AstInclude            *include;
-        AstBinding            *binding;
-        AstIf                 *static_if;
-        AstFunction           *function;
-        AstOverloadedFunction *overloaded_function;
-        AstGlobal             *global;
-        AstTyped              *expr;
-        AstStrLit             *strlit;
-        AstFileContents       *file_contents;
-        AstType               *type_alias;
-        AstEnumType           *enum_type;
-        AstEnumValue          *enum_value;
-        AstMemRes             *mem_res;
-        AstFunction           *poly_proc;
-        AstPolyQuery          *poly_query;
-        AstForeignBlock       *foreign_block;
-        AstMacro              *macro;
-        AstUse                *use;
-        AstInterface          *interface;
-        AstConstraint         *constraint;
-        AstDirectiveLibrary   *library;
-    };
-} Entity;
-
-typedef struct EntityHeap {
-    bh_arena entity_arena;
-    bh_arr(Entity *) entities;
-    i32 next_id;
-
-    i32 state_count[Entity_State_Count];
-    i32 type_count[Entity_Type_Count];
-
-    i32 all_count[Entity_State_Count][Entity_Type_Count];
-} EntityHeap;
-
-void entity_heap_init(EntityHeap* entities);
-void entity_heap_insert_existing(EntityHeap* entities, Entity* e);
-Entity* entity_heap_insert(EntityHeap* entities, Entity e);
-Entity* entity_heap_top(EntityHeap* entities);
-void entity_heap_change_top(EntityHeap* entities, Entity* new_top);
-void entity_heap_remove_top(EntityHeap* entities);
-void entity_change_type(EntityHeap* entities, Entity *ent, EntityType new_type);
-
-// If target_arr is null, the entities will be placed directly in the heap.
-void add_entities_for_node(bh_arr(Entity *)* target_arr, AstNode* node, Scope* scope, Package* package);
-
-void symres_entity(Entity* ent);
-void check_entity(Entity* ent);
-void emit_entity(Entity* ent);
-
-struct Package {
-    char *name;
-
-    Scope *scope;
-    Scope *private_scope;
-
-    // NOTE: This tracks all of the 'use package' statements of this package throughout
-    // the code base. This is used when a static if clears and new symbols are introduced.
-    // 'use package' statements have to be reevaluated to pull in the new symbols.
-    bh_arr(Entity *) use_package_entities;
-};
-
-typedef enum CompileAction CompileAction;
-enum CompileAction {
-    ONYX_COMPILE_ACTION_COMPILE,
-    ONYX_COMPILE_ACTION_RUN,
-    ONYX_COMPILE_ACTION_DOCUMENT,
-    ONYX_COMPILE_ACTION_PRINT_HELP,
-};
-
-
-// ROBUSTNESS: The Runtime definitions here must match those in build_opts.onyx!!
-typedef enum Runtime Runtime;
-enum Runtime {
-    Runtime_Unknown = 0,
-    Runtime_Onyx    = 1,
-    Runtime_Wasi    = 2,
-    Runtime_Js      = 3,
-    Runtime_Custom  = 4,
-};
-
-
-typedef struct CompileOptions CompileOptions;
-struct CompileOptions {
-    bh_allocator allocator;
-    CompileAction action;
-
-    u32 verbose_output          : 2;
-    b32 fun_output              : 1;
-    b32 print_function_mappings : 1;
-    b32 print_static_if_results : 1;
-    b32 print_notes             : 1;
-    b32 no_colors               : 1;
-    b32 no_file_contents        : 1;
-    
-    b32 use_post_mvp_features : 1;
-    b32 use_multi_threading   : 1;
-    b32 generate_foreign_info : 1;
-
-    Runtime runtime;
-
-    bh_arr(const char *) included_folders;
-    bh_arr(const char *) files;
-    const char* target_file;
-    const char* documentation_file;
-
-    b32 debug_enabled;
-
-    i32    passthrough_argument_count;
-    char** passthrough_argument_data;
-};
-
-typedef struct Context Context;
-struct Context {
-    Table(Package *)      packages;
-    EntityHeap            entities;
-
-    Scope *global_scope;
-
-    CompileOptions* options;
-
-    bh_arena                  ast_arena;
-    bh_allocator token_alloc, ast_alloc;
-
-    bh_arr(bh_file_contents) loaded_files;
-
-    // NOTE: This is defined in onyxwasm.h
-    struct OnyxWasmModule* wasm_module;
-
-    b32 cycle_almost_detected : 1;
-    b32 cycle_detected : 1;
-};
-
-extern Context context;
-
-// NOTE: Basic internal types constructed in the parser
-extern AstBasicType basic_type_void;
-extern AstBasicType basic_type_bool;
-extern AstBasicType basic_type_i8;
-extern AstBasicType basic_type_u8;
-extern AstBasicType basic_type_i16;
-extern AstBasicType basic_type_u16;
-extern AstBasicType basic_type_i32;
-extern AstBasicType basic_type_u32;
-extern AstBasicType basic_type_i64;
-extern AstBasicType basic_type_u64;
-extern AstBasicType basic_type_f32;
-extern AstBasicType basic_type_f64;
-extern AstBasicType basic_type_rawptr;
-extern AstBasicType basic_type_type_expr; // :TypeExprHack
-
-extern AstBasicType basic_type_int_unsized;
-extern AstBasicType basic_type_float_unsized;
-
-extern AstBasicType basic_type_i8x16;
-extern AstBasicType basic_type_i16x8;
-extern AstBasicType basic_type_i32x4;
-extern AstBasicType basic_type_i64x2;
-extern AstBasicType basic_type_f32x4;
-extern AstBasicType basic_type_f64x2;
-extern AstBasicType basic_type_v128;
-
-// HACK
-// :AutoReturnType
-extern Type type_auto_return;
-extern AstBasicType basic_type_auto_return;
-
-extern OnyxToken builtin_package_token;
-extern AstGlobal builtin_heap_start;
-extern AstGlobal builtin_stack_top;
-extern AstGlobal builtin_tls_base;
-extern AstGlobal builtin_tls_size;
-extern AstType  *builtin_string_type;
-extern AstType  *builtin_cstring_type;
-extern AstType  *builtin_range_type;
-extern Type     *builtin_range_type_type;
-extern AstType  *builtin_vararg_type;
-extern Type     *builtin_vararg_type_type;
-extern AstTyped *builtin_context_variable;
-extern AstType  *builtin_allocator_type;
-extern AstType  *builtin_iterator_type;
-extern AstType  *builtin_callsite_type;
-extern AstType  *builtin_any_type;
-extern AstType  *builtin_code_type;
-extern AstType  *builtin_link_options_type;
-extern AstTyped *type_table_node;
-extern AstTyped *foreign_blocks_node;
-extern AstType  *foreign_block_type;
-extern AstTyped *tagged_procedures_node;
-extern AstFunction *builtin_initialize_data_segments;
-extern AstFunction *builtin_run_init_procedures;
-extern bh_arr(AstFunction *) init_procedures;
-
-typedef struct BuiltinSymbol {
-    char*    package;
-    char*    sym;
-    AstNode* node;
-} BuiltinSymbol;
-
-extern const BuiltinSymbol builtin_symbols[];
-
-typedef struct IntrinsicMap {
-    char*         name;
-    OnyxIntrinsic intrinsic;
-} IntrinsicMap;
-
-typedef Table(OnyxIntrinsic) IntrinsicTable;
-extern IntrinsicTable intrinsic_table;
-
-extern bh_arr(OverloadOption) operator_overloads[Binary_Op_Count];
-
-void initialize_builtins(bh_allocator a);
-void initalize_special_globals();
-void introduce_build_options(bh_allocator a);
-
-
-// NOTE: Useful not inlined functions
-AstTyped* ast_reduce(bh_allocator a, AstTyped* node);
-AstNode* ast_clone(bh_allocator a, void* n);
-AstFunction* clone_function_header(bh_allocator a, AstFunction* func);
-void clone_function_body(bh_allocator a, AstFunction* dest, AstFunction* source);
-
-void promote_numlit_to_larger(AstNumLit* num);
-b32 convert_numlit_to_type(AstNumLit* num, Type* type);
-
-typedef enum TypeMatch {
-    TYPE_MATCH_SUCCESS,
-    TYPE_MATCH_FAILED,
-    TYPE_MATCH_YIELD,
-    TYPE_MATCH_SPECIAL, // Only used for nest polymorph function lookups
-} TypeMatch;
-#define unify_node_and_type(node, type) (unify_node_and_type_((node), (type), 1))
-TypeMatch unify_node_and_type_(AstTyped** pnode, Type* type, b32 permanent);
-Type* resolve_expression_type(AstTyped* node);
-
-i64   get_expression_integer_value(AstTyped* node, b32 *out_is_valid);
-char *get_expression_string_value(AstTyped* node, b32 *out_is_valid);
-
-b32 cast_is_legal(Type* from_, Type* to_, char** err_msg);
-char* get_function_name(AstFunction* func);
-
-AstNode* strip_aliases(AstNode* node);
-
-AstNumLit*       make_bool_literal(bh_allocator, b32 b);
-AstNumLit*       make_int_literal(bh_allocator a, i64 value);
-AstNumLit*       make_float_literal(bh_allocator a, f64 value);
-AstRangeLiteral* make_range_literal(bh_allocator a, AstTyped* low, AstTyped* high);
-AstBinaryOp*     make_binary_op(bh_allocator a, BinaryOp operation, AstTyped* left, AstTyped* right);
-AstArgument*     make_argument(bh_allocator a, AstTyped* value);
-AstFieldAccess*  make_field_access(bh_allocator a, AstTyped* node, char* field);
-AstAddressOf*    make_address_of(bh_allocator a, AstTyped* node);
-AstLocal*        make_local(bh_allocator a, OnyxToken* token, AstType* type_node);
-AstNode*         make_symbol(bh_allocator a, OnyxToken* sym);
-AstUnaryOp*      make_cast(bh_allocator a, AstTyped* expr, Type* to);
-AstZeroValue*    make_zero_value(bh_allocator a, OnyxToken *token, Type* type);
-
-void arguments_initialize(Arguments* args);
-b32 fill_in_arguments(Arguments* args, AstNode* provider, char** err_msg, b32 insert_zero_values);
-void arguments_ensure_length(Arguments* args, u32 count);
-void arguments_copy(Arguments* dest, Arguments* src);
-void arguments_clone(Arguments* dest, Arguments* src);
-void arguments_deep_clone(bh_allocator a, Arguments* dest, Arguments* src);
-void arguments_remove_baked(Arguments* args);
-void arguments_clear_baked_flags(Arguments* args);
-TypeMatch check_arguments_against_type(Arguments* args, TypeFunction* func_type, VarArgKind* va_kind,
-                                 OnyxToken* location, char* func_name, struct OnyxError* error);
-i32 get_argument_buffer_size(TypeFunction* type, Arguments* args);
-
-// GROSS: Using void* to avoid having to cast everything.
-const char* node_get_type_name(void* node);
-
-b32 static_if_resolution(AstIf* static_if);
-
-void insert_poly_sln_into_scope(Scope* scope, AstPolySolution *sln);
-TypeMatch find_polymorphic_sln(AstPolySolution *out, AstPolyParam *param, AstFunction *func, PolyProcLookupMethod pp_lookup, ptr actual, char** err_msg);
-AstFunction* polymorphic_proc_lookup(AstFunction* pp, PolyProcLookupMethod pp_lookup, ptr actual, OnyxToken* tkn);
-AstFunction* polymorphic_proc_solidify(AstFunction* pp, bh_arr(AstPolySolution) slns, OnyxToken* tkn);
-AstNode* polymorphic_proc_try_solidify(AstFunction* pp, bh_arr(AstPolySolution) slns, OnyxToken* tkn);
-AstFunction* polymorphic_proc_build_only_header(AstFunction* pp, PolyProcLookupMethod pp_lookup, ptr actual);
-AstFunction* polymorphic_proc_build_only_header_with_slns(AstFunction* pp, bh_arr(AstPolySolution) slns, b32 error_if_failed);
-b32 potentially_convert_function_to_polyproc(AstFunction *func);
-AstPolyCallType* convert_call_to_polycall(AstCall* call);
-
-void add_overload_option(bh_arr(OverloadOption)* poverloads, u64 precedence, AstTyped* overload);
-AstTyped* find_matching_overload_by_arguments(bh_arr(OverloadOption) overloads, Arguments* args);
-AstTyped* find_matching_overload_by_type(bh_arr(OverloadOption) overloads, Type* type);
-void report_unable_to_match_overload(AstCall* call, bh_arr(OverloadOption) overloads);
-
-void expand_macro(AstCall** pcall, AstFunction* template);
-AstFunction* macro_resolve_header(AstMacro* macro, Arguments* args, OnyxToken* callsite, b32 error_if_failed);
-
-Type* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(AstPolySolution) slns, OnyxFilePos pos, b32 error_if_failed);
-
-// NOTE: Useful inlined functions
-static inline b32 is_lval(AstNode* node) {
-    node = strip_aliases(node);
-
-    if    ((node->kind == Ast_Kind_Local)
-        || (node->kind == Ast_Kind_Param)
-        || (node->kind == Ast_Kind_Global)
-        || (node->kind == Ast_Kind_Dereference)
-        || (node->kind == Ast_Kind_Subscript)
-        || (node->kind == Ast_Kind_Field_Access)
-        || (node->kind == Ast_Kind_Memres)
-        || (node->kind == Ast_Kind_Constraint_Sentinel)) // Bit of a hack, but this makes constraints like 'T->foo()' work.
-        return 1;
-
-    if (node->kind == Ast_Kind_Compound) {
-        b32 all_lval = 1;
-        bh_arr_each(AstTyped *, expr, ((AstCompound *) node)->exprs) all_lval = all_lval && is_lval((AstNode *) *expr);
-        return all_lval;
-    }
-
-    return 0;
-}
-
-static inline b32 binop_is_assignment(BinaryOp binop) {
-    return (binop >= Binary_Op_Assign_Start && binop <= Binary_Op_Assign_End);
-}
-
-static inline b32 binop_is_compare(BinaryOp binop) {
-    return (binop >= Binary_Op_Equal && binop <= Binary_Op_Greater_Equal);
-}
-
-static inline b32 node_is_type(AstNode* node) {
-    node = strip_aliases(node);
-
-    return (node->kind > Ast_Kind_Type_Start) && (node->kind < Ast_Kind_Type_End);
-}
-
-static inline b32 node_is_auto_cast(AstNode* node) {
-    return (node->kind == Ast_Kind_Unary_Op) && (((AstUnaryOp *) node)->operation == Unary_Op_Auto_Cast);
-}
-
-static inline b32 node_is_addressable_literal(AstNode* node) {
-    return (node->kind == Ast_Kind_Struct_Literal)
-        || (node->kind == Ast_Kind_Array_Literal);
-}
-
-static inline Type* get_expression_type(AstTyped* expr) {
-    switch (expr->kind) {
-        case Ast_Kind_Block: case Ast_Kind_If: case Ast_Kind_While: return NULL;
-        default: return expr->type;
-    }
-}
-
-static inline CallingConvention type_function_get_cc(Type* type) {
-    if (type == NULL) return CC_Undefined;
-    if (type->kind != Type_Kind_Function) return CC_Undefined;
-    if (type_is_compound(type->Function.return_type)) return CC_Return_Stack;
-    return CC_Return_Wasm;
-}
-
-static inline ParamPassType type_get_param_pass(Type* type) {
-    if (type_is_structlike_strict(type) && !type_structlike_is_simple(type)) return Param_Pass_By_Implicit_Pointer;
-    return Param_Pass_By_Value;
-}
-
-static inline AstFunction* get_function_from_node(AstNode* node) {
-    if (node->kind == Ast_Kind_Function) return (AstFunction *) node;
-    if (node->kind == Ast_Kind_Polymorphic_Proc) return (AstFunction *) node;
-    if (node->kind == Ast_Kind_Macro) return get_function_from_node((AstNode*) ((AstMacro *) node)->body);
-    return NULL;
-}
-
-static inline void convert_polyproc_to_function(AstFunction *func) {
-    if (func->kind != Ast_Kind_Polymorphic_Proc) return;
-
-    func->kind = Ast_Kind_Function;
-    func->parent_scope_of_poly_proc = NULL;
-    func->poly_params = NULL;
-    func->known_slns = NULL;
-    func->concrete_funcs = NULL;
-    func->active_queries.hashes = NULL;
-    func->active_queries.entries = NULL;
-    func->poly_scope = NULL;
-    func->entity = NULL;
-    func->type = NULL;
-    func->tags = NULL;
-}
-
-static inline void convert_function_to_polyproc(AstFunction *func) {
-    if (func->kind != Ast_Kind_Function) return;
-
-    func->kind = Ast_Kind_Polymorphic_Proc;
-    func->parent_scope_of_poly_proc = func->scope->parent;
-    func->scope = NULL;
-    if (func->entity) entity_change_type(&context.entities, func->entity, Entity_Type_Polymorphic_Proc);
-}
-
-#endif // #ifndef ONYXASTNODES_H
diff --git a/include/bh.h b/include/bh.h
deleted file mode 100644 (file)
index 0049b26..0000000
+++ /dev/null
@@ -1,2787 +0,0 @@
-#ifndef BH_H
-#define BH_H
-
-// NOTE: For lseek64
-#define _LARGEFILE64_SOURCE
-
-#if defined(_WIN32) || defined(_WIN64)
-    #define _BH_WINDOWS 1
-#endif
-
-#if defined(__unix__)
-    #define _BH_LINUX 1
-#endif
-
-#include <sys/stat.h>
-#include <malloc.h>
-#include <time.h>
-
-#ifdef _BH_LINUX
-    #include <errno.h>
-    #include <fcntl.h>
-    #include <unistd.h>
-    #include <dirent.h>
-#endif
-
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h> // TODO: Replace with needed functions
-#include <stdint.h>
-#include <assert.h>
-#include <stdio.h>
-
-#if defined(_MSC_VER) && !defined(_WINDOWS_)
-    #include "small_windows.h"
-#endif
-
-//-------------------------------------------------------------------------------------
-// Better types
-//-------------------------------------------------------------------------------------
-typedef uint8_t u8;
-typedef uint16_t u16;
-typedef uint32_t u32;
-typedef uint64_t u64;
-typedef int8_t i8;
-typedef int16_t i16;
-typedef int32_t i32;
-typedef int64_t i64;
-typedef int64_t isize;
-typedef i32 b32;
-typedef void* ptr;
-typedef float f32;
-typedef double f64;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// Better character functions
-//-------------------------------------------------------------------------------------
-b32 char_is_alpha(const char a);
-b32 char_is_num(const char a);
-b32 char_is_alphanum(const char a);
-char charset_contains(const char* charset, char ch);
-b32 char_is_whitespace(const char a);
-b32 char_in_range(const char lo, const char hi, const char a);
-i64 chars_match(char* ptr1, char* ptr2);
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// Better math functions
-//-------------------------------------------------------------------------------------
-#define bh_max(a, b)         ((a) > (b) ? (a) : (b))
-#define bh_min(a, b)         ((a) < (b) ? (a) : (b))
-#define bh_clamp(v, a, b)    (bh_min((b), bh_max((a), (v))))
-#define bh_abs(x)            ((x) < 0 ? -(x) : (x))
-#define size_of(x)           (isize) sizeof(x)
-
-static inline u64 log2_dumb(u64 n) {
-    switch (n) {
-    case 1 << 0:  return 0;
-    case 1 << 1:  return 1;
-    case 1 << 2:  return 2;
-    case 1 << 3:  return 3;
-    case 1 << 4:  return 4;
-    case 1 << 5:  return 5;
-    case 1 << 6:  return 6;
-    case 1 << 7:  return 7;
-    case 1 << 8:  return 8;
-    case 1 << 9:  return 9;
-    case 1 << 10: return 10;
-    case 1 << 11: return 11;
-    case 1 << 12: return 12;
-    case 1 << 13: return 13;
-    case 1 << 14: return 14;
-    case 1 << 15: return 15;
-    case 1 << 16: return 16;
-    case 1 << 17: return 17;
-    case 1 << 18: return 18;
-    case 1 << 19: return 19;
-    case 1 << 20: return 20;
-    case 1 << 21: return 21;
-    case 1 << 22: return 22;
-    case 1 << 23: return 23;
-    case 1 << 24: return 24;
-    case 1 << 25: return 25;
-    case 1 << 26: return 26;
-    case 1 << 27: return 27;
-    case 1 << 28: return 28;
-    case 1 << 29: return 29;
-    case 1 << 30: return 30;
-    case 1 << 31: return 31;
-
-    default:   return 0;
-    }
-}
-
-
-
-static inline const char* bh_num_suffix(u64 i) {
-    if (i == 11 || i == 12 || i == 13) return "th";
-
-    switch (i % 10) {
-        case 0: return "th";
-        case 1: return "st";
-        case 2: return "nd";
-        case 3: return "rd";
-        case 4: return "th";
-        case 5: return "th";
-        case 6: return "th";
-        case 7: return "th";
-        case 8: return "th";
-        case 9: return "th";
-
-        default: return "";
-    }
-}
-
-
-
-
-//-------------------------------------------------------------------------------------
-// Conversion functions
-//-------------------------------------------------------------------------------------
-
-// Converts an unsigned integer to the unsigned LEB128 format
-u8* uint_to_uleb128(u64 n, i32* output_length);
-u8* int_to_leb128(i64 n, i32* output_length);
-u8* float_to_ieee754(f32 f, b32 reverse);
-u8* double_to_ieee754(f64 f, b32 reverse);
-
-u64 uleb128_to_uint(u8* bytes, i32 *byte_walker);
-
-
-
-//-------------------------------------------------------------------------------------
-// Helpful macros
-//-------------------------------------------------------------------------------------
-#define bh_offset_of(Type, elem)        ((isize)&(((Type)*) 0)->elem)
-#define bh_align_of(Type)                bh_offset_of(struct { char c; Type member; }, member)
-#define bh_swap(Type, a, b)                do { Type tmp = (a); (a) = (b); (b) = tmp; } while(0)
-
-#define bh_align(x, a) if ((x) % (a) != 0) (x) += (a) - ((x) % (a));
-
-#define bh_pointer_add(ptr, amm)        ((void *)((u8 *) (ptr) + (amm)))
-#define BH_BIT(x)                        (1 << (x))
-#define BH_MASK_SET(var, set, mask)     ((set) ? ((var) |= (mask)) : ((var) &= ~(mask)))
-
-#define fori(var, lo, hi)                 for (i64 var = (lo); var < (hi); var++)
-#define forir(var, hi, lo)                for (i64 var = (hi); var >= (lo); var--)
-#define forll(T, var, start, step)        for (T* var = (start); var != NULL; var = (T *) var->step)
-
-#if defined(BH_DEBUG) && !defined(_BH_WINDOWS)
-    #define DEBUG_HERE                        __asm("int $3")
-#else
-    #define DEBUG_HERE
-#endif
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// Custom allocators
-//-------------------------------------------------------------------------------------
-
-typedef enum bh_allocator_actions {
-    bh_allocator_action_alloc,
-    bh_allocator_action_free,
-    bh_allocator_action_resize,
-} bh_allocator_actions;
-
-#define BH_ALLOCATOR_PROC(name) \
-ptr name(ptr data, bh_allocator_actions action, \
-         isize size, isize alignment, \
-         void* prev_memory, \
-         u64 flags)
-
-typedef BH_ALLOCATOR_PROC(bh__allocator_proc); // NOTE: so bh__allocator_proc can be used instead of that type
-
-typedef struct bh_allocator {
-    bh__allocator_proc* proc; // Procedure that can handle bh_allocator_actions
-    ptr                    data; // Pointer to the other data for the allocator
-} bh_allocator;
-
-typedef enum bh_allocator_flags {
-    bh_allocator_flag_clear = 1    // Sets all memory to be 0
-} bh_allocator_flags;
-
-ptr bh_alloc(bh_allocator a, isize size);
-ptr bh_alloc_aligned(bh_allocator a, isize size, isize alignment);
-ptr bh_resize(bh_allocator a, ptr data, isize new_size);
-ptr bh_resize_aligned(bh_allocator a, ptr data, isize new_size, isize alignment);
-void bh_free(bh_allocator a, ptr data);
-
-#define bh_alloc_item(allocator_, T)                (T *) bh_alloc(allocator_, sizeof(T))
-#define bh_alloc_array(allocator_, T, n)            (T *) bh_alloc(allocator_, sizeof(T) * (n))
-
-// NOTE: This should get optimized out since alignment should be a power of two
-#define bh__align(x, alignment)                        ((((x) / alignment) + 1) * alignment)
-
-
-
-
-// HEAP ALLOCATOR
-// Essentially a wrapper for malloc, free and realloc
-bh_allocator bh_heap_allocator(void);
-BH_ALLOCATOR_PROC(bh_heap_allocator_proc);
-
-
-
-
-
-// ARENA ALLOCATOR
-typedef struct bh_arena {
-    bh_allocator backing;
-    ptr first_arena, current_arena;
-    isize size, arena_size; // in bytes
-} bh_arena;
-
-typedef struct bh__arena_internal {
-    ptr next_arena;
-    void* data; // Not actually a pointer, just used for the offset
-} bh__arena_internal;
-
-void bh_arena_init(bh_arena* alloc, bh_allocator backing, isize arena_size);
-void bh_arena_free(bh_arena* alloc);
-bh_allocator bh_arena_allocator(bh_arena* alloc);
-BH_ALLOCATOR_PROC(bh_arena_allocator_proc);
-
-
-
-
-
-
-// SCRATCH ALLOCATOR
-typedef struct bh_scratch {
-    bh_allocator backing;
-    ptr memory, end, curr;
-} bh_scratch;
-
-void bh_scratch_init(bh_scratch* scratch, bh_allocator backing, isize scratch_size);
-void bh_scratch_free(bh_scratch* scratch);
-bh_allocator bh_scratch_allocator(bh_scratch* scratch);
-BH_ALLOCATOR_PROC(bh_scratch_allocator_proc);
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// Allocator based string functions
-//-------------------------------------------------------------------------------------
-
-b32 bh_str_starts_with(char* str, char* start);
-b32 bh_str_ends_with(char* str, char* end);
-char* bh_strdup(bh_allocator a, char* str);
-
-
-
-
-
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// Better files
-//-------------------------------------------------------------------------------------
-#ifndef BH_NO_FILE
-
-typedef enum bh_file_error {
-    BH_FILE_ERROR_NONE,
-    BH_FILE_ERROR_INVALID,
-    BH_FILE_ERROR_BAD_FD,
-} bh_file_error;
-
-typedef enum bh_file_mode {
-    BH_FILE_MODE_READ = 1 << 0,
-    BH_FILE_MODE_WRITE = 1 << 1,
-    BH_FILE_MODE_APPEND = 1 << 2,
-    BH_FILE_MODE_RW = 1 << 3,
-
-    BH_FILE_MODE_MODES = BH_FILE_MODE_READ | BH_FILE_MODE_WRITE | BH_FILE_MODE_APPEND | BH_FILE_MODE_RW
-} bh_file_mode;
-
-typedef enum bh_file_whence {
-    BH_FILE_WHENCE_BEGIN = SEEK_SET,
-    BH_FILE_WHENCE_CURRENT = SEEK_CUR,
-    BH_FILE_WHENCE_END = SEEK_END,
-} bh_file_whence;
-
-#ifdef _BH_WINDOWS
-    typedef HANDLE bh_file_descriptor;
-#else
-    typedef int bh_file_descriptor;
-#endif
-
-typedef struct bh_file {
-    bh_file_descriptor fd;
-    char const* filename;
-} bh_file;
-
-typedef enum bh_file_standard {
-    BH_FILE_STANDARD_INPUT,
-    BH_FILE_STANDARD_OUTPUT,
-    BH_FILE_STANDARD_ERROR
-} bh_file_standard;
-
-typedef struct bh_file_contents {
-    bh_allocator allocator;
-    const char *filename;
-    isize length;
-    void* data;
-    isize line_count;
-} bh_file_contents;
-
-
-#define BH_FILE_TYPE_DIRECTORY 3
-#define BH_FILE_TYPE_FILE      4
-#define BH_FILE_TYPE_LINK      5
-typedef struct bh_file_stats {
-    isize size;
-    u32 file_type;
-} bh_file_stats;
-
-bh_file_error bh_file_get_standard(bh_file* file, bh_file_standard stand);
-
-bh_file_error bh_file_create(bh_file* file, char const* filename);
-bh_file_error bh_file_open(bh_file* file, char const* filename);
-bh_file_error bh_file_open_mode(bh_file* file, bh_file_mode mode, const char* filename);
-bh_file_error bh_file_new(bh_file* file, bh_file_descriptor fd, const char* filename);
-b32 bh_file_read_at(bh_file* file, i64 offset, void* buffer, isize buff_size, isize* bytes_read);
-b32 bh_file_write_at(bh_file* file, i64 offset, void const* buffer, isize buff_size, isize* bytes_wrote);
-i64 bh_file_seek(bh_file* file, i64 offset, bh_file_whence whence);
-i64 bh_file_seek_to(bh_file* file, i64 offset);
-i64 bh_file_seek_to_end(bh_file* file);
-i64 bh_file_skip(bh_file* file, i64 bytes);
-i64 bh_file_tell(bh_file* file);
-bh_file_error bh_file_close(bh_file* file);
-i32 bh_file_read(bh_file* file, void* buffer, isize buff_size);
-i32 bh_file_write(bh_file* file, void* buffer, isize buff_size);
-void bh_file_flush(bh_file* file);
-i64 bh_file_size(bh_file* file);
-b32 bh_file_stat(char const* filename, bh_file_stats* stat);
-b32 bh_file_exists(char const* filename);
-b32 bh_file_remove(char const* filename);
-char* bh_path_get_full_name(char const* filename, bh_allocator a);
-char* bh_path_get_parent(char const* filename, bh_allocator a);
-char* bh_path_convert_separators(char* path);
-
-// This function returns a volatile pointer. Do not store it without copying!
-// `included_folders` is bh_arr(const char *).
-char* bh_lookup_file(char* filename, char* relative_to, char *suffix, b32 add_suffix, const char ** included_folders, b32 search_included_folders);
-
-#define bh_file_read_contents(allocator_, x) _Generic((x), \
-    bh_file*: bh_file_read_contents_bh_file, \
-    const char*: bh_file_read_contents_direct, \
-    char*: bh_file_read_contents_direct)((allocator_), x)
-
-bh_file_contents bh_file_read_contents_bh_file(bh_allocator alloc, bh_file* file);
-bh_file_contents bh_file_read_contents_direct(bh_allocator alloc, const char* filename);
-i32 bh_file_contents_free(bh_file_contents* contents);
-
-
-#ifdef _BH_WINDOWS
-    typedef struct Windows_Directory_Opened {
-        HANDLE hndl;
-        WIN32_FIND_DATAA found_file;
-    } Windows_Directory_Opened;
-
-    typedef Windows_Directory_Opened *bh_dir;
-#else
-    typedef DIR *bh_dir;
-#endif
-
-typedef enum bh_dirent_type {
-    BH_DIRENT_UNKNOWN,
-    BH_DIRENT_BLOCK,
-    BH_DIRENT_CHAR,
-    BH_DIRENT_DIRECTORY,
-    BH_DIRENT_FILE,
-    BH_DIRENT_SYMLINK,
-    BH_DIRENT_OTHER,
-} bh_dirent_type;
-
-typedef struct bh_dirent {
-    bh_dirent_type type;
-    u32 id;
-    u32 name_length;
-    char name[256];
-} bh_dirent;
-
-bh_dir bh_dir_open(char* path);
-b32    bh_dir_read(bh_dir dir, bh_dirent* out);
-void   bh_dir_close(bh_dir dir);
-
-#endif
-
-
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// Alternate printing
-//-------------------------------------------------------------------------------------
-// Barebones implementation of printf. Does not support all format options
-// Currently supports:
-//         %c - chars
-//        %_(u)d - ints where _ is:
-//            nothing - decimal
-//            o - octal
-//            x - hexadecimal
-//        %_(u)l - longs where _ is:
-//            nothing - decimal
-//            o - octal
-//            x - hexadecimal
-//        %f - floating points
-//        %s - null terminated strings
-//        %p - pointers
-//        %% - literal %
-
-typedef struct bh__print_format {
-    u32 base;
-} bh__print_format;
-
-isize bh_printf(char const *fmt, ...);
-isize bh_printf_va(char const *fmt, va_list va);
-isize bh_printf_err(char const *fmt, ...);
-isize bh_printf_err_va(char const *fmt, va_list va);
-isize bh_fprintf(bh_file* f, char const *fmt, ...);
-isize bh_fprintf_va(bh_file* f, char const *fmt, va_list va);
-char* bh_bprintf(char const *fmt, ...);
-char* bh_bprintf_va(char const *fmt, va_list va);
-char* bh_aprintf(bh_allocator alloc, const char* fmt, ...);
-char* bh_aprintf_va(bh_allocator alloc, const char* fmt, va_list va);
-isize bh_snprintf(char *str, isize n, char const *fmt, ...);
-isize bh_snprintf_va(char *str, isize n, char const *fmt, va_list va);
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// Flexible buffer
-//-------------------------------------------------------------------------------------
-
-typedef struct bh_buffer {
-    bh_allocator allocator;
-    i32 length, capacity;
-    u8* data;
-} bh_buffer;
-
-#ifndef BH_BUFFER_GROW_FORMULA
-#define BH_BUFFER_GROW_FORMULA(x)            ((x) > 0 ? ((x) << 1) : 16)
-#endif
-
-void bh_buffer_init(bh_buffer* buffer, bh_allocator alloc, i32 length);
-void bh_buffer_free(bh_buffer* buffer);
-void bh_buffer_clear(bh_buffer* buffer);
-void bh_buffer_grow(bh_buffer* buffer, i32 length);
-void bh_buffer_append(bh_buffer* buffer, const void * data, i32 length);
-void bh_buffer_concat(bh_buffer* buffer, bh_buffer other);
-void bh_buffer_write_byte(bh_buffer* buffer, u8 byte);
-void bh_buffer_write_u32(bh_buffer* buffer, u32 i);
-void bh_buffer_write_u64(bh_buffer* buffer, u64 i);
-void bh_buffer_align(bh_buffer* buffer, u32 alignment);
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// Better dynamically-sized arrays
-//-------------------------------------------------------------------------------------
-#ifndef BH_NO_ARRAY
-
-typedef struct bh__arr {
-    bh_allocator allocator;
-    i32 length, capacity;
-} bh__arr;
-
-#ifndef BH_ARR_GROW_FORMULA
-#define BH_ARR_GROW_FORMULA(x)       ((x) > 0 ? ((x) << 1) : 4)
-#endif
-
-#define bh_arr(T)                    T*
-#define bh__arrhead(arr)             (((bh__arr *)(arr)) - 1)
-
-#define bh_arr_allocator(arr)        (arr ? bh__arrhead(arr)->allocator : bh_heap_allocator())
-#define bh_arr_length(arr)           (arr ? bh__arrhead(arr)->length : 0)
-#define bh_arr_capacity(arr)         (arr ? bh__arrhead(arr)->capacity : 0)
-#define bh_arr_size(arr)             (arr ? bh__arrhead(arr)->capacity * sizeof(*(arr)) : 0)
-#define bh_arr_valid(arr, i)         (arr ? (i32)(i) < bh__arrhead(arr)->length : 0)
-
-#define bh_arr_pop(arr)              ((arr)[--bh__arrhead(arr)->length])
-#define bh_arr_last(arr)             ((arr)[bh__arrhead(arr)->length - 1])
-#define bh_arr_end(arr, i)           ((i) >= &(arr)[bh_arr_length(arr)])
-#define bh_arr_start(arr, i)         ((i) < (arr))
-
-#define bh_arr_new(allocator_, arr, cap)    (bh__arr_grow((allocator_), (void**) &(arr), sizeof(*(arr)), cap))
-#define bh_arr_free(arr)                    (bh__arr_free((void**) &(arr)))
-#define bh_arr_copy(allocator_, arr)        (bh__arr_copy((allocator_), (arr), sizeof(*(arr))))
-
-#define bh_arr_grow(arr, cap)          (bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), cap))
-#define bh_arr_shrink(arr, cap)        (bh__arr_shrink((void **) &(arr), sizeof(*(arr)), cap))
-#define bh_arr_set_length(arr, n)      (bh__arrhead(arr)->length = n)
-
-#define bh_arr_insertn(arr, i, n)      (bh__arr_insertn((void **) &(arr), sizeof(*(arr)), i, n))
-
-#define bh_arr_insert_end(arr, n)      ( \
-    bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), bh_arr_length(arr) + n), \
-    bh__arrhead(arr)->length += n)
-
-#define bh_arr_push(arr, value)       ( \
-    bh_arr_length(arr) + 1 > bh_arr_capacity(arr) ? bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), bh_arr_length(arr) + 1) : 0, \
-    arr[bh__arrhead(arr)->length++] = value)
-
-#define bh_arr_set_at(arr, n, value) ( \
-    bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), (n) + 1), \
-    bh_arr_set_length((arr), bh_max(bh_arr_length(arr), (i32) (n) + 1)), \
-    arr[n] = value)
-
-#define bh_arr_is_empty(arr)          (arr ? bh__arrhead(arr)->length == 0 : 1)
-#define bh_arr_clear(arr)             (arr ? (bh__arrhead(arr)->length = 0) : 0)
-
-#define bh_arr_deleten(arr, i, n)     (bh__arr_deleten((void **) &(arr), sizeof(*(arr)), i, n))
-#define bh_arr_fastdelete(arr, i)     (arr[i] = arr[--bh__arrhead(arr)->length])
-
-#define bh_arr_each(T, var, arr)      for (T* var = (arr); !bh_arr_end((arr), var); var++)
-#define bh_arr_rev_each(T, var, arr)  for (T* var = &bh_arr_last((arr)); !bh_arr_start((arr), var); var--)
-
-#define bh_arr_zero(arr) memset(arr, 0, bh_arr_length(arr) * sizeof(*(arr)));
-
-b32 bh__arr_grow(bh_allocator alloc, void** arr, i32 elemsize, i32 cap);
-b32 bh__arr_shrink(void** arr, i32 elemsize, i32 cap);
-b32 bh__arr_free(void **arr);
-void* bh__arr_copy(bh_allocator alloc, void *arr, i32 elemsize);
-void bh__arr_insertn(void **arr, i32 elemsize, i32 index, i32 numelems);
-void bh__arr_deleten(void **arr, i32 elemsize, i32 index, i32 numelems);
-
-#endif
-
-
-
-
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// STRING HASH TABLE FUNCTIONS
-//-------------------------------------------------------------------------------------
-#ifndef BH_NO_TABLE
-
-#ifdef BH_DEFINE
-u64 bh__table_hash_function(const char* str, i32 len, i32 mod) {
-    u64 hash = 5381;
-    i32 c, l = 0;
-    if (len == 0) len = ((u32) 1 << 31) - 1;
-
-    while ((c = *str++) && l++ < len) {
-        hash = (hash << 5) + hash + c;
-    }
-
-    return hash % mod;
-}
-#endif
-
-typedef struct bh_table_iterator {
-    ptr *tab, *endtab;
-    i32 elemsize, arrlen;
-    ptr entry;
-} bh_table_iterator;
-
-typedef struct bh__table {
-    bh_allocator allocator;
-    u64 table_size; // NOTE: u64 since padding will make it 8-bytes no matter what
-    ptr arrs[];
-} bh__table;
-
-#define bh_table(T)        T*
-
-#ifdef BH_TABLE_SIZE_SAFE
-    #define bh_table_init(allocator_, tab, hs)    bh__table_init(allocator_, (bh__table **)&(tab), hs)
-    #define bh_table_free(tab)                    bh__table_free((bh__table **)&(tab))
-    #define bh_table_put(T, tab, key, value)      (assert(sizeof(T) == sizeof(*(tab))), (*((T *) bh__table_put((bh__table *) tab, sizeof(T), key)) = (T) value))
-    #define bh_table_has(T, tab, key)             (assert(sizeof(T) == sizeof(*(tab))), (bh__table_has((bh__table *) tab, sizeof(T), key)))
-    #define bh_table_get(T, tab, key)             (assert(sizeof(T) == sizeof(*(tab))), (*((T *) bh__table_get((bh__table *) tab, sizeof(T), key))))
-    #define bh_table_delete(T, tab, key)          (assert(sizeof(T) == sizeof(*(tab))), bh__table_delete((bh__table *) tab, sizeof(T), key))
-    #define bh_table_clear(tab)                   (bh__table_clear((bh__table *) tab))
-
-    #define bh_table_iter_setup(T, tab)           (assert(sizeof(T) == sizeof(*(tab))), bh__table_iter_setup((bh__table *) tab, sizeof(T)))
-    #define bh_table_iter_key(it)                 ((char *)(bh_pointer_add(it.entry, it.elemsize + sizeof(u16))))
-    #define bh_table_iter_value(T, it)            (*(T *)it.entry)
-#else
-    #define bh_table_init(allocator_, tab, hs)    bh__table_init(allocator_, (bh__table **)&(tab), hs)
-    #define bh_table_free(tab)                    bh__table_free((bh__table **)&(tab))
-    #define bh_table_put(T, tab, key, value)      (*((T *) bh__table_put((bh__table *) tab, sizeof(T), key)) = value)
-    #define bh_table_has(T, tab, key)             (bh__table_has((bh__table *) tab, sizeof(T), key))
-    #define bh_table_get(T, tab, key)             (*((T *) bh__table_get((bh__table *) tab, sizeof(T), key)))
-    #define bh_table_delete(T, tab, key)          (bh__table_delete((bh__table *) tab, sizeof(T), key))
-    #define bh_table_clear(tab)                   (bh__table_clear((bh__table *) tab))
-
-    #define bh_table_iter_setup(T, tab)           (bh__table_iter_setup((bh__table *) tab, sizeof(T)))
-    #define bh_table_iter_key(it)                 ((char *)(bh_pointer_add(it.entry, it.elemsize + sizeof(u16))))
-    #define bh_table_iter_value(T, it)            (*(T *)it.entry)
-#endif
-
-#define bh_table_each_start(T, table) { \
-    bh_table_iterator it = bh_table_iter_setup(T, (table)); \
-    while (bh_table_iter_next(&it)) { \
-        const char* key = bh_table_iter_key(it); \
-        T value = bh_table_iter_value(T, it);
-#define bh_table_each_end            } }
-
-b32 bh__table_init(bh_allocator allocator, bh__table **table, i32 table_size);
-b32 bh__table_free(bh__table **table);
-ptr bh__table_put(bh__table *table, i32 elemsize, char *key);
-b32 bh__table_has(bh__table *table, i32 elemsize, char *key);
-ptr bh__table_get(bh__table *table, i32 elemsize, char *key);
-void bh__table_delete(bh__table *table, i32 elemsize, char *key);
-void bh__table_clear(bh__table *table);
-bh_table_iterator bh__table_iter_setup(bh__table *table, i32 elemsize);
-b32 bh_table_iter_next(bh_table_iterator* it);
-
-#endif
-// Using stb_ds for tables now because they are better in every single way.
-#define Table(T) struct { char *key; T value; } *
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------
-// IMAP (integer to integer map)
-//-------------------------------------------------------------------------------
-#ifndef BH_NO_IMAP
-
-typedef u64 bh_imap_entry_t;
-
-typedef struct bh__imap_lookup_result {
-    i64 hash_index;
-    i64 entry_prev;
-    i64 entry_index;
-} bh__imap_lookup_result;
-
-typedef struct bh__imap_entry {
-    bh_imap_entry_t key, value;
-    i64 next;
-} bh__imap_entry;
-
-typedef struct bh_imap {
-    bh_allocator allocator;
-
-    bh_arr(i64)            hashes;
-    bh_arr(bh__imap_entry) entries;
-} bh_imap;
-
-
-void bh_imap_init(bh_imap* imap, bh_allocator alloc, i32 hash_count);
-void bh_imap_free(bh_imap* imap);
-void bh_imap_put(bh_imap* imap, bh_imap_entry_t key, bh_imap_entry_t value);
-b32 bh_imap_has(bh_imap* imap, bh_imap_entry_t key);
-bh_imap_entry_t bh_imap_get(bh_imap* imap, bh_imap_entry_t key);
-void bh_imap_delete(bh_imap* imap, bh_imap_entry_t key);
-void bh_imap_clear(bh_imap* imap);
-
-#ifdef BH_DEFINE
-#endif // BH_DEFINE
-
-
-#endif
-
-
-
-
-
-
-
-
-
-
-
-// MANAGED HEAP ALLOCATOR
-typedef struct bh_managed_heap {
-    bh_imap ptrs;
-} bh_managed_heap;
-
-void bh_managed_heap_init(bh_managed_heap* mh);
-void bh_managed_heap_free(bh_managed_heap* mh);
-bh_allocator bh_managed_heap_allocator(bh_managed_heap* mh);
-BH_ALLOCATOR_PROC(bh_managed_heap_allocator_proc);
-
-
-
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------
-// OTHER COMMON DATA STRUCTURES
-//-------------------------------------------------------------------------------
-#ifndef BH_NO_DATASTRUCTURES
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-#endif // BH_NO_DATASTRUCTURES
-
-
-
-
-
-//------------------------------------------------------------------------------
-// TIME / DURATION
-//------------------------------------------------------------------------------
-u64 bh_time_curr();
-u64 bh_time_duration(u64 old);
-
-
-
-
-
-
-
-
-
-
-
-#ifdef BH_DEFINE
-
-#undef BH_DEFINE
-//-------------------------------------------------------------------------------------
-// IMPLEMENTATIONS
-//-------------------------------------------------------------------------------------
-
-//-------------------------------------------------------------------------------------
-// CHAR FUNCTIONS
-//-------------------------------------------------------------------------------------
-
-b32 char_is_alpha(const char a) {
-    return ('a' <= a && a <= 'z') || ('A' <= a && a <= 'Z');
-}
-
-char charset_contains(const char* charset, char ch) {
-    while (*charset) {
-        if (*charset == ch) return ch;
-        charset++;
-    }
-
-    return 0;
-}
-
-b32 char_is_num(const char a) {
-    return ('0' <= a && a <= '9');
-}
-
-b32 char_is_alphanum(const char a) {
-    return char_is_alpha(a) || char_is_num(a);
-}
-
-b32 char_is_whitespace(const char a) {
-    return charset_contains(" \t\r\n", a);
-}
-
-b32 char_in_range(const char lo, const char hi, const char a) {
-    return lo <= a && a <= hi;
-}
-
-i64 chars_match(char* ptr1, char* ptr2) {
-    i64 len = 0;
-    while (*ptr2 != '\0' && *ptr1 == *ptr2) ptr1++, ptr2++, len++;
-    return *ptr2 == '\0' ? len : 0;
-}
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// CUSTOM ALLOCATORS IMPLEMENTATION
-//-------------------------------------------------------------------------------------
-ptr bh_alloc(bh_allocator a, isize size) {
-    return bh_alloc_aligned(a, size, 16);
-}
-
-ptr bh_alloc_aligned(bh_allocator a, isize size, isize alignment) {
-    return a.proc(a.data, bh_allocator_action_alloc, size, alignment, NULL,  0);
-}
-
-ptr bh_resize(bh_allocator a, ptr data, isize new_size) {
-    return bh_resize_aligned(a, data, new_size, 16);
-}
-
-ptr bh_resize_aligned(bh_allocator a, ptr data, isize new_size, isize alignment) {
-    return a.proc(a.data, bh_allocator_action_resize, new_size, alignment, data, 0);
-}
-
-void bh_free(bh_allocator a, ptr data) {
-    if (data != NULL) a.proc(a.data, bh_allocator_action_free, 0, 0, data, 0);
-}
-
-
-
-// HEAP ALLOCATOR IMPLEMENTATION
-bh_allocator bh_heap_allocator(void) {
-    return (bh_allocator) {
-        .proc = bh_heap_allocator_proc,
-        .data = NULL
-    };
-}
-
-BH_ALLOCATOR_PROC(bh_heap_allocator_proc) {
-    ptr retval = NULL;
-
-    switch (action) {
-    case bh_allocator_action_alloc: {
-#if defined(_BH_WINDOWS)
-        retval = _aligned_malloc(size, alignment);
-#elif defined(_BH_LINUX)
-        i32 success = posix_memalign(&retval, alignment, size);
-#endif
-        if (flags & bh_allocator_flag_clear && retval != NULL) {
-            memset(retval, 0, size);
-        }
-    } break;
-
-    case bh_allocator_action_resize: {
-        // TODO: Maybe replace with better custom function
-#if defined(_BH_WINDOWS)
-        retval = _aligned_realloc(prev_memory, size, alignment);
-#elif defined(_BH_LINUX)
-        retval = realloc(prev_memory, size);
-#endif
-    } break;
-
-    case bh_allocator_action_free: {
-#if defined(_BH_WINDOWS)
-        _aligned_free(prev_memory);
-#elif defined(_BH_LINUX)
-        free(prev_memory);
-#endif
-    } break;
-    }
-
-    return retval;
-}
-
-
-
-
-
-
-// MANAGED HEAP ALLOCATOR IMPLEMENTATION
-void bh_managed_heap_init(bh_managed_heap* mh) {
-    bh_imap_init(&mh->ptrs, bh_heap_allocator(), 512);
-}
-
-void bh_managed_heap_free(bh_managed_heap* mh) {
-    bh_arr_each(bh__imap_entry, p, mh->ptrs.entries) {
-#if defined(_BH_WINDOWS)
-        _aligned_free((void *) p->key);
-#elif defined(_BH_LINUX)
-        free((void *) p->key);
-#endif
-    }
-
-    bh_imap_free(&mh->ptrs);
-}
-
-bh_allocator bh_managed_heap_allocator(bh_managed_heap* mh) {
-    return (bh_allocator) {
-        .proc = bh_managed_heap_allocator_proc,
-        .data = mh
-    };
-}
-
-BH_ALLOCATOR_PROC(bh_managed_heap_allocator_proc) {
-    bh_managed_heap* mh = (bh_managed_heap *) data;
-    ptr retval = NULL;
-
-    switch (action) {
-    case bh_allocator_action_alloc: {
-#if defined(_BH_WINDOWS)
-        retval = _aligned_malloc(size, alignment);
-#elif defined(_BH_LINUX)
-        i32 success = posix_memalign(&retval, alignment, size);
-#endif
-
-        if (flags & bh_allocator_flag_clear && retval != NULL) {
-            memset(retval, 0, size);
-        }
-
-        if (retval != NULL)
-            bh_imap_put(&mh->ptrs, (u64) retval, 1);
-    } break;
-
-    case bh_allocator_action_resize: {
-        bh_imap_delete(&mh->ptrs, (u64) prev_memory);
-#if defined(_BH_WINDOWS)
-        retval = _aligned_realloc(prev_memory, size, alignment);
-#elif defined(_BH_LINUX)
-        retval = realloc(prev_memory, size);
-#endif
-        bh_imap_put(&mh->ptrs, (u64) retval, 1);
-    } break;
-
-    case bh_allocator_action_free: {
-        bh_imap_delete(&mh->ptrs, (u64) prev_memory);
-#if defined(_BH_WINDOWS)
-        _aligned_free(prev_memory);
-#elif defined(_BH_LINUX)
-        free(prev_memory);
-#endif
-    } break;
-    }
-
-    return retval;
-}
-
-
-
-
-
-
-
-
-// ARENA ALLOCATOR IMPLEMENTATION
-void bh_arena_init(bh_arena* alloc, bh_allocator backing, isize arena_size) {
-    arena_size = bh_max(arena_size, size_of(ptr));
-    ptr data = bh_alloc(backing, arena_size);
-
-    alloc->backing = backing;
-    alloc->arena_size = arena_size;
-    alloc->size = sizeof(ptr);
-    alloc->first_arena = data;
-    alloc->current_arena = data;
-
-    ((bh__arena_internal *)(alloc->first_arena))->next_arena = NULL;
-}
-
-void bh_arena_free(bh_arena* alloc) {
-    bh__arena_internal *walker = (bh__arena_internal *) alloc->first_arena;
-    bh__arena_internal *trailer = walker;
-    while (walker != NULL) {
-        walker = walker->next_arena;
-        bh_free(alloc->backing, trailer);
-        trailer = walker;
-    }
-
-    alloc->first_arena = NULL;
-    alloc->current_arena = NULL;
-    alloc->arena_size = 0;
-    alloc->size = 0;
-}
-
-bh_allocator bh_arena_allocator(bh_arena* alloc) {
-    return (bh_allocator) {
-        .proc = bh_arena_allocator_proc,
-        .data = alloc,
-    };
-}
-
-BH_ALLOCATOR_PROC(bh_arena_allocator_proc) {
-    bh_arena* alloc_arena = (bh_arena*) data;
-
-    ptr retval = NULL;
-
-    switch (action) {
-    case bh_allocator_action_alloc: {
-        bh_align(size, alignment);
-        bh_align(alloc_arena->size, alignment);
-
-        if (size > alloc_arena->arena_size - size_of(ptr)) {
-            // Size too large for the arena
-            return NULL;
-        }
-
-        if (alloc_arena->size + size >= alloc_arena->arena_size) {
-            bh__arena_internal* new_arena = (bh__arena_internal *) bh_alloc(alloc_arena->backing, alloc_arena->arena_size);
-
-            if (new_arena == NULL) {
-                bh_printf_err("Arena Allocator: couldn't allocate new arena");
-                return NULL;
-            }
-
-            new_arena->next_arena = NULL;
-            ((bh__arena_internal *)(alloc_arena->current_arena))->next_arena = new_arena;
-            alloc_arena->current_arena = new_arena;
-            alloc_arena->size = sizeof(ptr);
-        }
-
-        retval = bh_pointer_add(alloc_arena->current_arena, alloc_arena->size);
-        alloc_arena->size += size;
-    } break;
-
-    case bh_allocator_action_resize: {
-        // Do nothing since this is a fixed allocator
-    } break;
-
-    case bh_allocator_action_free: {
-        // Do nothing since this allocator isn't made for freeing memory
-    } break;
-    }
-
-    return retval;
-}
-
-
-
-
-// SCRATCH ALLOCATOR IMPLEMENTATION
-void bh_scratch_init(bh_scratch* scratch, bh_allocator backing, isize scratch_size) {
-    ptr memory = bh_alloc(backing, scratch_size);
-
-    scratch->backing = backing;
-    scratch->memory = memory;
-    scratch->curr = memory;
-    scratch->end = bh_pointer_add(memory, scratch_size);
-}
-
-void bh_scratch_free(bh_scratch* scratch) {
-    bh_free(scratch->backing, scratch->memory);
-
-    scratch->memory = NULL;
-    scratch->curr = NULL;
-    scratch->end = NULL;
-}
-
-bh_allocator bh_scratch_allocator(bh_scratch* scratch) {
-    return (bh_allocator) {
-        .proc = bh_scratch_allocator_proc,
-        .data = scratch,
-    };
-}
-
-BH_ALLOCATOR_PROC(bh_scratch_allocator_proc) {
-    bh_scratch* scratch = (bh_scratch*) data;
-    ptr retval = NULL;
-
-    switch (action) {
-    case bh_allocator_action_alloc: {
-        if (size > ((u8 *) scratch->end) - ((u8 *) scratch->memory)) {
-            return NULL;
-        }
-
-        retval = scratch->curr;
-        scratch->curr = bh_pointer_add(scratch->curr, size);
-
-        if (scratch->curr >= scratch->end) {
-            scratch->curr = scratch->memory;
-            retval = scratch->curr;
-        }
-    } break;
-
-    case bh_allocator_action_free: break;
-
-    case bh_allocator_action_resize: {
-        if (size > ((u8 *) scratch->end) - ((u8 *) scratch->memory)) {
-            return NULL;
-        }
-
-        retval = scratch->curr;
-        scratch->curr = bh_pointer_add(scratch->curr, size);
-
-        if (scratch->curr >= scratch->end) {
-            scratch->curr = scratch->memory;
-            retval = scratch->curr;
-        }
-
-        // HACK!!!!!: Using size instead of some kind of "old size"
-        memcpy(retval, prev_memory, size);
-    } break;
-    }
-
-    return retval;
-}
-
-
-
-
-//-------------------------------------------------------------------------------------
-// CONVERSION FUNCTIONS IMPLEMENTATION
-//-------------------------------------------------------------------------------------
-u8* uint_to_uleb128(u64 n, i32* output_length) {
-    static u8 buffer[16];
-
-    *output_length = 0;
-    u8* output = buffer;
-    u8 byte;
-    do {
-        byte = n & 0x7f;
-        n >>= 7;
-        if (n != 0) byte |= (1 << 7);
-        *output++ = byte;
-        (*output_length)++;
-    } while (n != 0);
-
-    return buffer;
-}
-
-
-// Converts a signed integer to the signed LEB128 format
-u8* int_to_leb128(i64 n, i32* output_length) {
-    static u8 buffer[16];
-
-    *output_length = 0;
-    u8* output = buffer;
-    b32 more = 1;
-
-    i32 size = 64;
-
-    u8 byte;
-    do {
-        byte = n & 0x7f;
-        n >>= 7;
-
-        more = !(((n == 0) && (byte & 0x40) == 0) || ((n == -1) && (byte & 0x40) != 0));
-        if (more)
-            byte |= 0x80;
-        *output++ = byte;
-        (*output_length)++;
-    } while (more);
-
-    return buffer;
-}
-
-// NOTE: This assumes the underlying implementation of float on the host
-// system is already IEEE-754. This is safe to assume in most cases.
-u8* float_to_ieee754(f32 f, b32 reverse) {
-    static u8 buffer[4];
-
-    u8* fmem = (u8*) &f;
-    if (reverse) {
-        buffer[0] = fmem[3];
-        buffer[1] = fmem[2];
-        buffer[2] = fmem[1];
-        buffer[3] = fmem[0];
-    } else {
-        buffer[0] = fmem[0];
-        buffer[1] = fmem[1];
-        buffer[2] = fmem[2];
-        buffer[3] = fmem[3];
-    }
-
-    return buffer;
-}
-
-u8* double_to_ieee754(f64 f, b32 reverse) {
-    static u8 buffer[8];
-
-    u8* fmem = (u8*) &f;
-    if (reverse) {
-        buffer[0] = fmem[7];
-        buffer[1] = fmem[6];
-        buffer[2] = fmem[5];
-        buffer[3] = fmem[4];
-        buffer[4] = fmem[3];
-        buffer[5] = fmem[2];
-        buffer[6] = fmem[1];
-        buffer[7] = fmem[0];
-    } else {
-        buffer[0] = fmem[0];
-        buffer[1] = fmem[1];
-        buffer[2] = fmem[2];
-        buffer[3] = fmem[3];
-        buffer[4] = fmem[4];
-        buffer[5] = fmem[5];
-        buffer[6] = fmem[6];
-        buffer[7] = fmem[7];
-    }
-
-    return buffer;
-}
-
-u64 uleb128_to_uint(u8* bytes, i32 *byte_count) {
-    u64 res = 0;
-    u64 shift = 0;
-
-    while (1) {
-        u8 byte = bytes[(*byte_count)++];
-        res |= (byte & 0x7f) << shift;
-        if ((byte & 0x80) == 0) break;
-        shift += 7;
-    }
-    return res;
-}
-
-
-
-//-------------------------------------------------------------------------------------
-// STRING IMPLEMENTATION
-//-------------------------------------------------------------------------------------
-b32 bh_str_starts_with(char* str, char* start) {
-    char* s = str;
-    char* p = start;
-
-    while (*s != '\0' && *p != '\0' && *s == *p) s++, p++;
-
-    return *p == '\0';
-}
-
-b32 bh_str_ends_with(char* str, char* end) {
-    i32 slen = strlen(str);
-    i32 elen = strlen(end);
-
-    char* s = str + slen - 1;
-    char* e = end + elen - 1;
-
-    while (*e == *s && e != end && s != str) e--, s--;
-
-    return *e == *s;
-}
-
-char* bh_strdup(bh_allocator a, char* str) {
-    u32 len = strlen(str);
-    char* buf = bh_alloc(a, len + 1);
-
-    char* t = buf;
-    while ((*t++ = *str++));
-    return buf;
-}
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// FILE IMPLEMENTATION
-//-------------------------------------------------------------------------------------
-#ifndef BH_NO_FILE
-
-static b32 bh__file_seek_wrapper(bh_file_descriptor fd, i64 offset, bh_file_whence whence, i64* new_offset);
-
-bh_file_error bh_file_get_standard(bh_file* file, bh_file_standard stand) {
-    const char* filename = NULL;
-
-#if defined(_BH_WINDOWS)
-    bh_file_descriptor sd_fd;
-
-    switch (stand) {
-    case BH_FILE_STANDARD_INPUT:
-        sd_fd = GetStdHandle(STD_INPUT_HANDLE);
-        filename = "stdin";
-        break;
-    case BH_FILE_STANDARD_OUTPUT:
-        sd_fd = GetStdHandle(STD_OUTPUT_HANDLE);
-        filename = "stdout";
-        break;
-    case BH_FILE_STANDARD_ERROR:
-        sd_fd = GetStdHandle(STD_ERROR_HANDLE);
-        filename = "stderr";
-        break;
-    default:
-        return BH_FILE_ERROR_BAD_FD;
-    }
-    file->fd = sd_fd;
-
-#elif defined(_BH_LINUX)
-    i32 sd_fd = -1;
-
-    switch (stand) {
-    case BH_FILE_STANDARD_INPUT:
-        sd_fd = STDIN_FILENO;
-        filename = "stdin"; // These are constants in the data section so everything should be okay
-        break;
-    case BH_FILE_STANDARD_OUTPUT:
-        sd_fd = STDOUT_FILENO;
-        filename = "stdout";
-        break;
-    case BH_FILE_STANDARD_ERROR:
-        sd_fd = STDERR_FILENO;
-        filename = "stderr";
-        break;
-    default:
-        return BH_FILE_ERROR_BAD_FD;
-    }
-
-    file->fd = sd_fd;
-#endif
-
-
-    file->filename = filename;
-    return BH_FILE_ERROR_NONE;
-}
-
-bh_file_error bh_file_create(bh_file* file, const char* filename) {
-    // Need to do this to avoid compiler complaining about types
-    bh_file_mode write_rw = (bh_file_mode) (BH_FILE_MODE_WRITE | BH_FILE_MODE_RW);
-    return bh_file_open_mode(file, write_rw, filename);
-}
-
-bh_file_error bh_file_open(bh_file* file, const char* filename) {
-    return bh_file_open_mode(file, BH_FILE_MODE_READ, filename);
-}
-
-bh_file_error bh_file_open_mode(bh_file* file, bh_file_mode mode, const char* filename) {
-#if defined(_BH_WINDOWS)
-    DWORD desired_access;
-    DWORD creation_disposition;
-
-    switch (mode & BH_FILE_MODE_MODES) {
-        case BH_FILE_MODE_READ:
-            desired_access = GENERIC_READ;
-            creation_disposition = OPEN_EXISTING;
-            break;
-        case BH_FILE_MODE_WRITE:
-            desired_access = GENERIC_WRITE;
-            creation_disposition = CREATE_ALWAYS;
-            break;
-        case BH_FILE_MODE_APPEND:
-            desired_access = GENERIC_WRITE;
-            creation_disposition = OPEN_ALWAYS;
-            break;
-        case BH_FILE_MODE_READ | BH_FILE_MODE_RW:
-            desired_access = GENERIC_READ | GENERIC_WRITE;
-            creation_disposition = OPEN_EXISTING;
-            break;
-        case BH_FILE_MODE_WRITE | BH_FILE_MODE_RW:
-            desired_access = GENERIC_READ | GENERIC_WRITE;
-            creation_disposition = CREATE_ALWAYS;
-            break;
-        case BH_FILE_MODE_APPEND | BH_FILE_MODE_RW:
-            desired_access = GENERIC_READ | GENERIC_WRITE;
-            creation_disposition = OPEN_ALWAYS;
-            break;
-        default:
-            return BH_FILE_ERROR_INVALID;
-    }
-
-
-    file->fd = CreateFileA(filename,
-                    desired_access,
-                    FILE_SHARE_READ,
-                    NULL,
-                    creation_disposition,
-                    FILE_ATTRIBUTE_NORMAL,
-                    NULL);
-
-    if (file->fd == INVALID_HANDLE_VALUE) {
-        return BH_FILE_ERROR_INVALID;
-    }
-
-    file->filename = filename;
-    return BH_FILE_ERROR_NONE;
-
-#elif defined(_BH_LINUX)
-    i32 os_mode = 0;
-
-    switch (mode & BH_FILE_MODE_MODES) {
-    case BH_FILE_MODE_READ:                       os_mode = O_RDONLY; break;
-    case BH_FILE_MODE_WRITE:                      os_mode = O_WRONLY | O_CREAT | O_TRUNC; break;
-    case BH_FILE_MODE_APPEND:                     os_mode = O_WRONLY | O_APPEND | O_CREAT; break;
-    case BH_FILE_MODE_READ   | BH_FILE_MODE_RW:   os_mode = O_RDWR; break;
-    case BH_FILE_MODE_WRITE  | BH_FILE_MODE_RW:   os_mode = O_RDWR | O_CREAT | O_TRUNC; break;
-    case BH_FILE_MODE_APPEND | BH_FILE_MODE_RW:   os_mode = O_RDWR | O_APPEND | O_CREAT; break;
-    //default: // TODO Handle errors
-    }
-
-    file->fd = open(filename, os_mode,
-        S_IRUSR | S_IWUSR | S_IROTH | S_IWOTH | S_IRGRP | S_IWGRP //+rw-rw-rw-
-    );
-    if (file->fd < 0) {
-        return BH_FILE_ERROR_INVALID;
-    }
-
-    // TODO: Set this using some allocator
-    file->filename = filename;
-
-    return BH_FILE_ERROR_NONE;
-#endif
-}
-
-bh_file_error bh_file_new(bh_file* file, bh_file_descriptor fd, const char* filename) {
-    file->filename = filename; // This may be unsafe
-    file->fd = fd;
-    return BH_FILE_ERROR_NONE;
-}
-
-b32 bh_file_read_at(bh_file* file, i64 offset, void* buffer, isize buff_size, isize* bytes_read) {
-#if defined(_BH_WINDOWS)
-    bh_file_seek_to(file, offset);
-    BOOL res = ReadFile(file->fd, buffer, buff_size, (i32 *) bytes_read, NULL);
-    if (res) return 1;
-    else     return 0;
-
-#elif defined(_BH_LINUX)
-    if (file->fd == 0) {
-        isize res = read(file->fd, buffer, buff_size);
-        if (res < 0) return 0;
-        if (bytes_read) *bytes_read = res;
-        return 1;
-    }
-
-    isize res = pread(file->fd, buffer, buff_size, offset);
-    if (res < 0) return 0;
-    if (bytes_read) *bytes_read = res;
-    return 1;
-#endif
-}
-
-b32 bh_file_write_at(bh_file* file, i64 offset, void const* buffer, isize buff_size, isize* bytes_wrote) {
-    isize res;
-    i64 current_offset = 0;
-    bh__file_seek_wrapper(file->fd, 0, BH_FILE_WHENCE_CURRENT, &current_offset);
-
-#if defined(_BH_WINDOWS)
-    if (current_offset != offset) bh_file_seek_to(file, offset);
-    res = (isize) WriteFile(file->fd, buffer, buff_size, (i32 *) bytes_wrote, NULL);
-    return res;
-
-#elif defined(_BH_LINUX)
-    if (current_offset == offset || file->fd == 1 || file->fd == 2) {
-        // Standard in and out do like pwrite()
-        res = write(file->fd, buffer, buff_size);
-    } else {
-        res = pwrite(file->fd, buffer, buff_size, offset);
-    }
-    if (res < 0) return 0;
-    if (bytes_wrote) *bytes_wrote = res;
-
-    return 1;
-#endif
-}
-
-static b32 bh__file_seek_wrapper(bh_file_descriptor fd, i64 offset, bh_file_whence whence, i64* new_offset) {
-#if defined(_BH_WINDOWS)
-    LARGE_INTEGER new_file_pointer;
-    LARGE_INTEGER dest;
-    dest.QuadPart = offset;
-
-    BOOL res = SetFilePointerEx(fd, dest, &new_file_pointer, whence);
-    *new_offset = new_file_pointer.QuadPart;
-
-    return res;
-
-#elif defined(_BH_LINUX)
-    i64 res = lseek64(fd, offset, whence);
-    if (res < 0) return 0;
-    if (new_offset) *new_offset = res;
-    return 1;
-#endif
-}
-
-// Returns new offset
-i64 bh_file_seek(bh_file* file, i64 offset, bh_file_whence whence) {
-    i64 new_offset = -1;
-    bh__file_seek_wrapper(file->fd, offset, whence, &new_offset);
-    return new_offset;
-}
-
-i64 bh_file_seek_to(bh_file* file, i64 offset) {
-    i64 new_offset = -1;
-    bh__file_seek_wrapper(file->fd, offset, BH_FILE_WHENCE_BEGIN, &new_offset);
-    return new_offset;
-}
-
-i64 bh_file_seek_to_end(bh_file* file) {
-    i64 new_offset = -1;
-    bh__file_seek_wrapper(file->fd, 0, BH_FILE_WHENCE_END, &new_offset);
-    return new_offset;
-}
-
-i64 bh_file_skip(bh_file* file, i64 bytes) {
-    i64 new_offset = 0;
-    bh__file_seek_wrapper(file->fd, bytes, BH_FILE_WHENCE_CURRENT, &new_offset);
-    return new_offset;
-}
-
-i64 bh_file_tell(bh_file* file) {
-    i64 new_offset = 0;
-    bh__file_seek_wrapper(file->fd, 0, BH_FILE_WHENCE_CURRENT, &new_offset);
-    return new_offset;
-}
-
-bh_file_error bh_file_close(bh_file* file) {
-    bh_file_error err = BH_FILE_ERROR_NONE;
-
-#if defined(_BH_WINDOWS)
-    BOOL success = CloseHandle(file->fd);
-    if (!success) err = BH_FILE_ERROR_INVALID;
-
-    return err;
-
-#elif defined(_BH_LINUX)
-    i32 res = close(file->fd);
-    if (res < 0)
-        err = BH_FILE_ERROR_INVALID;
-
-    return err;
-#endif
-}
-
-b32 bh_file_read(bh_file* file, void* buffer, isize buff_size) {
-    return bh_file_read_at(file, bh_file_tell(file), buffer, buff_size, NULL);
-}
-
-b32 bh_file_write(bh_file* file, void* buffer, isize buff_size) {
-    return bh_file_write_at(file, bh_file_tell(file), buffer, buff_size, NULL);
-}
-
-void bh_file_flush(bh_file* file) {
-    #ifdef _BH_LINUX
-    fdatasync(file->fd);
-    #endif
-}
-
-i64 bh_file_size(bh_file* file) {
-    i64 size = 0;
-    i64 prev = bh_file_tell(file);
-    bh_file_seek_to_end(file);
-    size = bh_file_tell(file);
-    bh_file_seek_to(file, prev);
-    return size;
-}
-
-bh_file_contents bh_file_read_contents_bh_file(bh_allocator alloc, bh_file* file) {
-    bh_file_contents fc = {
-        .allocator = alloc,
-        .filename  = bh_strdup(alloc, (char *) file->filename),
-        .length = 0, .data = NULL, .line_count = 0,
-    };
-
-    isize size = bh_file_size(file);
-    if (size <= 0) return fc;
-
-    fc.data = bh_alloc(alloc, size + 1);
-    fc.length = size;
-    bh_file_read_at(file, 0, fc.data, fc.length, NULL);
-    ((u8*) fc.data)[fc.length] = '\0';
-
-    return fc;
-}
-
-bh_file_contents bh_file_read_contents_direct(bh_allocator alloc, const char* filename) {
-    bh_file file;
-    bh_file_open(&file, filename);
-    bh_file_contents fc = bh_file_read_contents(alloc, &file);
-    bh_file_close(&file);
-    return fc;
-}
-
-b32 bh_file_contents_free(bh_file_contents* contents) {
-    bh_free(contents->allocator, contents->data);
-    contents->length = 0;
-    return 1;
-}
-
-b32 bh_file_stat(char const* filename, bh_file_stats* out) {
-    struct stat s;
-    if (stat(filename, &s) == -1) {
-        return 0;
-    }
-
-    out->size = s.st_size;
-
-    if ((s.st_mode & S_IFMT) == S_IFDIR) out->file_type = BH_FILE_TYPE_DIRECTORY;
-    if ((s.st_mode & S_IFMT) == S_IFREG) out->file_type = BH_FILE_TYPE_FILE;
-
-#if defined(_BH_LINUX) 
-    if ((s.st_mode & S_IFMT) == S_IFLNK) out->file_type = BH_FILE_TYPE_LINK;
-#endif
-
-    return 1;
-}
-
-b32 bh_file_exists(char const* filename) {
-    struct stat s;
-    return stat(filename, &s) != -1;
-}
-
-b32 bh_file_remove(char const* filename) {
-#if defined(_BH_WINDOWS)
-    return DeleteFileA(filename);
-
-#elif defined(_BH_LINUX)
-    return unlink(filename) == 0;
-#endif
-}
-
-char* bh_path_get_full_name(char const* filename, bh_allocator a) {
-#if defined(_BH_WINDOWS)
-    char buffer[4096];
-    GetFullPathNameA(filename, 4096, buffer, NULL);
-
-    i32 len = strlen(buffer);
-    char* result = bh_alloc_array(a, char, len + 1);
-    memmove(result, buffer, len);
-    result[len] = 0;
-
-    return result;
-
-#elif defined(_BH_LINUX)
-    char* p = realpath(filename, NULL);    
-
-    // Check if the file did not exists.
-    // :Cleanup should this return NULL?
-    if (p == NULL) return (char *) filename;
-    
-    i32 len = strlen(p);
-    char* result = bh_alloc_array(a, char, len + 1);
-    memmove(result, p, len);
-    result[len] = 0;
-
-    free(p);
-
-    return result;
-#endif
-}
-
-// NOTE: This assumes the filename is the full path, not relative to anything else.
-#if defined(_BH_LINUX)
-    #define DIR_SEPARATOR '/'
-#elif defined(_BH_WINDOWS)
-    #define DIR_SEPARATOR '\\'
-#endif
-char* bh_path_get_parent(char const* filename, bh_allocator a) {
-
-    char* result = bh_strdup(a, (char *) filename);
-    char* end = result + strlen(result);
-    while (*end != DIR_SEPARATOR && end != result) *end-- = '\0';
-
-    return result;
-}
-
-// This function returns a volatile pointer. Do not store it without copying!
-char* bh_lookup_file(char* filename, char* relative_to, char *suffix, b32 add_suffix, bh_arr(const char *) included_folders, b32 search_included_folders) {
-    assert(relative_to != NULL);
-
-    static char path[512];
-    fori (i, 0, 512) path[i] = 0;
-
-    static char fn[256];
-    fori (i, 0, 256) fn[i] = 0;
-
-    if (!bh_str_ends_with(filename, suffix) && add_suffix) {
-        bh_snprintf(fn, 256, "%s%s", filename, suffix);
-    } else {
-        bh_snprintf(fn, 256, "%s", filename);
-    }
-
-    fori (i, 0, 256) if (fn[i] == '/') fn[i] = DIR_SEPARATOR;
-
-    if (bh_str_starts_with(filename, "./")) {
-        if (relative_to[strlen(relative_to) - 1] != DIR_SEPARATOR)
-            bh_snprintf(path, 512, "%s%c%s", relative_to, DIR_SEPARATOR, fn + 2);
-        else
-            bh_snprintf(path, 512, "%s%s", relative_to, fn + 2);
-
-        if (bh_file_exists(path)) return bh_path_get_full_name(path, bh_heap_allocator());
-
-        return fn;
-    }
-
-    if (search_included_folders) {
-        bh_arr_each(const char *, folder, included_folders) {
-            if ((*folder)[strlen(*folder) - 1] != DIR_SEPARATOR)
-                bh_snprintf(path, 512, "%s%c%s", *folder, DIR_SEPARATOR, fn);
-            else
-                bh_snprintf(path, 512, "%s%s", *folder, fn);
-
-            if (bh_file_exists(path)) return bh_path_get_full_name(path, bh_heap_allocator());
-        }
-    }
-
-    return fn;
-}
-
-//
-// Modifies the path in-place.
-char* bh_path_convert_separators(char* path) {
-#if defined(_BH_LINUX)
-    #define DIR_SEPARATOR '/'
-    #define OTHER_SEPARATOR '\\'
-#elif defined(_BH_WINDOWS)
-    #define DIR_SEPARATOR '\\'
-    #define OTHER_SEPARATOR '/'
-#endif
-
-    fori (i, 0, (i64) strlen(path)) {
-        if (path[i] == OTHER_SEPARATOR) {
-            path[i] = DIR_SEPARATOR;
-        }
-    }
-
-    return path;
-}
-
-
-bh_dir bh_dir_open(char* path) {
-#ifdef _BH_WINDOWS
-    char new_path[512] = { 0 };
-    strncpy(new_path, path, 511);
-    bh_path_convert_separators(new_path);
-    strncat(new_path, "\\*.*", 511);
-
-    Windows_Directory_Opened* dir = malloc(sizeof(Windows_Directory_Opened));
-    dir->hndl = FindFirstFileA(new_path, &dir->found_file);
-    if (dir->hndl == INVALID_HANDLE_VALUE) {
-        return NULL;
-    }
-
-    return dir;
-#endif
-
-#ifdef _BH_LINUX
-    DIR* dir = opendir(path);
-    return dir;
-#endif
-}
-
-b32 bh_dir_read(bh_dir dir, bh_dirent* out) {
-
-#ifdef _BH_WINDOWS
-    do {
-        BOOL success = FindNextFileA(dir->hndl, &dir->found_file);
-        if (!success) return 0;
-    } while (!strcmp(dir->found_file.cFileName, ".") || !strcmp(dir->found_file.cFileName, ".."));
-
-    if (out == NULL) return 1;
-
-    out->type = (dir->found_file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
-        ? BH_DIRENT_DIRECTORY : BH_DIRENT_FILE;
-    out->id = 0;
-    out->name_length = strlen(dir->found_file.cFileName);
-    strncpy(out->name, dir->found_file.cFileName, 256);
-
-    return 1;
-#endif
-
-#ifdef _BH_LINUX
-    struct dirent *ent;
-    while (1) {
-        ent = readdir(dir);
-        if (ent == NULL) return 0;
-
-        // Skip the current directory and parent directory
-        if (strcmp(ent->d_name, ".") && strcmp(ent->d_name, "..")) break;
-    }
-
-    bh_dirent_type type = 0;
-    switch (ent->d_type) {
-        case DT_UNKNOWN: break;
-        case DT_BLK: type = BH_DIRENT_BLOCK; break;
-        case DT_CHR: type = BH_DIRENT_CHAR; break;
-        case DT_DIR: type = BH_DIRENT_DIRECTORY; break;
-        case DT_LNK: type = BH_DIRENT_SYMLINK; break;
-        case DT_REG: type = BH_DIRENT_FILE; break;
-        default: type = BH_DIRENT_OTHER; break;
-    }
-
-    if (out == NULL) return 1;
-
-    out->type = type;
-    out->id = (u32) ent->d_ino;
-    out->name_length = strlen(ent->d_name);
-    strncpy(out->name, ent->d_name, 256);
-
-    return 1;
-#endif
-}
-
-void bh_dir_close(bh_dir dir) {
-#ifdef _BH_WINDOWS
-    if (dir == NULL) return;
-
-    FindClose(dir->hndl);
-    free(dir);
-#endif
-
-#ifdef _BH_LINUX
-    if (dir == NULL) return;
-    closedir(dir);
-#endif
-}
-
-#undef DIR_SEPARATOR
-
-#endif // ifndef BH_NO_FILE
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// ALTERNATE PRINTF IMPLEMENTATION
-//-------------------------------------------------------------------------------------
-isize bh_printf(char const *fmt, ...) {
-    isize res;
-    va_list va;
-    va_start(va, fmt);
-    res = bh_printf_va(fmt, va);
-    va_end(va);
-    return res;
-}
-
-isize bh_printf_va(char const *fmt, va_list va) {
-    bh_file file;
-    bh_file_get_standard(&file, BH_FILE_STANDARD_OUTPUT);
-    return bh_fprintf_va(&file, fmt, va);
-}
-
-isize bh_printf_err(char const *fmt, ...) {
-    isize res;
-    va_list va;
-    va_start(va, fmt);
-    res = bh_printf_err_va(fmt, va);
-    va_end(va);
-    return res;
-}
-
-isize bh_printf_err_va(char const *fmt, va_list va) {
-    bh_file file;
-    bh_file_get_standard(&file, BH_FILE_STANDARD_ERROR);
-    return bh_fprintf_va(&file, fmt, va);
-}
-
-isize bh_fprintf(bh_file* f, char const *fmt, ...) {
-    isize res;
-    va_list va;
-    va_start(va, fmt);
-    res = bh_fprintf_va(f, fmt, va);
-    va_end(va);
-    return res;
-}
-
-isize bh_fprintf_va(bh_file* f, char const *fmt, va_list va) {
-    static char buffer[4096];
-    isize len = bh_snprintf_va(buffer, sizeof(buffer), fmt, va);
-    bh_file_write(f, buffer, len - 1);
-    return len;
-}
-
-char* bh_bprintf(char const *fmt, ...) {
-    char* res;
-    va_list va;
-    va_start(va, fmt);
-    res = bh_bprintf_va(fmt, va);
-    va_end(va);
-    return res;
-}
-
-char* bh_bprintf_va(char const *fmt, va_list va) {
-    static char buffer[4096];
-    isize len = bh_snprintf_va(buffer, sizeof(buffer), fmt, va);
-    buffer[len - 1] = 0;
-    return buffer;
-}
-
-char* bh_aprintf(bh_allocator alloc, const char* fmt, ...) {
-    char* res;
-    va_list va;
-    va_start(va, fmt);
-    res = bh_aprintf_va(alloc, fmt, va);
-    va_end(va);
-    return res;
-}
-
-char* bh_aprintf_va(bh_allocator alloc, const char* fmt, va_list va) {
-    static char buffer[4096];
-    isize len = bh_snprintf_va(buffer, sizeof(buffer), fmt, va);
-    char* res = bh_alloc(alloc, len);
-    memcpy(res, buffer, len);
-    res[len - 1] = 0;
-    return res;
-}
-
-isize bh_snprintf(char *str, isize n, char const *fmt, ...) {
-    isize res;
-    va_list va;
-    va_start(va, fmt);
-    res = bh_snprintf_va(str, n, fmt, va);
-    va_end(va);
-    return res;
-}
-
-isize bh__print_string(char* dest, isize n, char* src) {
-    isize len = 0;
-    while (n-- && (*dest++ = *src++)) len++;
-    return len;
-}
-
-isize bh__printu64(char* str, isize n, bh__print_format format, u64 value) {
-    char buf[128];
-    buf[127] = 0;
-    char* walker = buf + 127;
-    u32 base = format.base ? format.base : 10, tmp;
-
-    while (value > 0) {
-        tmp = value % base;
-        if (tmp > 9) {
-            switch (tmp) {
-            case 10: tmp = 'a'; break;
-            case 11: tmp = 'b'; break;
-            case 12: tmp = 'c'; break;
-            case 13: tmp = 'd'; break;
-            case 14: tmp = 'e'; break;
-            case 15: tmp = 'f'; break;
-            }
-        } else {
-            tmp += '0';
-        }
-
-        *--walker = tmp;
-        value /= base;
-    }
-
-    if (format.base == 16) {
-        *--walker = 'x';
-        *--walker = '0';
-    }
-
-    return bh__print_string(str, n, walker);
-}
-
-isize bh__printi64(char* str, isize n, bh__print_format format, i64 value) {
-    char buf[128];
-    buf[127] = 0;
-    char* walker = buf + 127;
-    u32 base = format.base ? format.base : 10, tmp;
-
-    b32 negative = value < 0 ? 1 : 0;
-    if (negative) value = -value;
-
-    if (value == 0) {
-        *--walker = '0';
-    } else {
-        while (value > 0) {
-            tmp = value % base;
-            if (tmp > 9) {
-                switch (tmp) {
-                case 10: tmp = 'a'; break;
-                case 11: tmp = 'b'; break;
-                case 12: tmp = 'c'; break;
-                case 13: tmp = 'd'; break;
-                case 14: tmp = 'e'; break;
-                case 15: tmp = 'f'; break;
-                }
-            } else {
-                tmp += '0';
-            }
-
-            *--walker = tmp;
-            value /= base;
-        }
-    }
-
-    if (negative) {
-        *--walker = '-';
-    }
-
-    if (format.base == 16) {
-        *--walker = 'x';
-        *--walker = '0';
-    }
-
-    return bh__print_string(str, n, walker);
-}
-
-// TODO: This implementation is VERY VERY BAD AND WRONG. Fix it.
-isize bh__printf64(char* str, isize n, f64 value) {
-    fori (i, 0, 6) value *= 10.0;
-    i64 v = (i64) value;
-
-    isize l1 = bh__printi64(str, n, ((bh__print_format) { .base = 10 }), v / 1000000);
-    str += l1;
-    n -= l1;
-
-    *str = '.';
-    str += 1;
-    n -= 1;
-
-    isize l2 = bh__printi64(str, n, ((bh__print_format) { .base = 10 }), bh_abs(v) % 1000000);
-
-    return l1 + l2 + 1;
-}
-
-// TODO: This is very hacked together but for now it will work.
-isize bh_snprintf_va(char *str, isize n, char const *fmt, va_list va) {
-    char const *text_start = str;
-    isize res;
-
-    while (*fmt) {
-        bh__print_format format = { 0 };
-        isize len = 0;
-
-        while (*fmt && *fmt != '%') {
-            *(str++) = *(fmt++);
-        }
-
-        if (!*fmt) goto end_of_format;
-
-        fmt++;
-
-        switch (*fmt++) {
-        case 'o': format.base = 8; break;
-        case 'x': format.base = 16; break;
-        default: fmt--;
-        }
-
-        switch (*fmt) {
-        case 'c': {
-            char c = (char) va_arg(va, int);
-            *(str++) = c;
-        } break;
-
-        case 'd': {
-            i64 value = (i64) va_arg(va, int);
-            len = bh__printi64(str, n, format, value);
-        } break;
-
-        case 'l': {
-            i64 value = (i64) va_arg(va, long);
-            len = bh__printi64(str, n, format, value);
-        } break;
-
-        case 'p': {
-            u64 value = (u64) va_arg(va, ptr);
-            format.base = 16;
-            len = bh__printu64(str, n, format, value);
-        } break;
-
-        case 's': {
-            char* s = va_arg(va, char *);
-            len = bh__print_string(str, n, s);
-        } break;
-
-        case 'b': { // String with a length (not null terminated)
-            char* s = va_arg(va, char *);
-            i32 l = va_arg(va, int);
-            len = bh__print_string(str, bh_min(l, n), s);
-        } break;
-
-        case 'f': {
-            f64 f = va_arg(va, f64);
-            len = bh__printf64(str, n, f);
-        } break;
-
-        default: fmt--;
-        }
-
-        fmt++;
-
-end_of_format:
-        str += len;
-        n -= len;
-    }
-
-    return str - text_start + 1;
-}
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// FLEXIBLE BUFFER IMPLEMENTATION
-//-------------------------------------------------------------------------------------
-#ifndef BH_NO_BUFFER
-
-void bh_buffer_init(bh_buffer* buffer, bh_allocator alloc, i32 init_size) {
-    buffer->allocator = alloc;
-    buffer->length = 0;
-    buffer->capacity = init_size;
-    buffer->data = bh_alloc(alloc, init_size);
-}
-
-void bh_buffer_free(bh_buffer* buffer) {
-    bh_free(buffer->allocator, buffer->data);
-    buffer->length = 0;
-    buffer->capacity = 0;
-}
-
-void bh_buffer_clear(bh_buffer* buffer) {
-    buffer->length = 0;
-}
-
-void bh_buffer_grow(bh_buffer* buffer, i32 length) {
-    if (buffer == NULL) return;
-
-    if (buffer->capacity >= length) {
-        // NOTE: Already have enough room
-        return;
-    }
-
-    i32 newcap = buffer->capacity;
-    while (newcap < length) newcap = BH_BUFFER_GROW_FORMULA(newcap);
-
-    ptr new_data = bh_resize(buffer->allocator, buffer->data, newcap);
-    if (new_data == NULL) return;
-
-    buffer->capacity = newcap;
-    buffer->data = new_data;
-}
-
-void bh_buffer_append(bh_buffer* buffer, const void * data, i32 length) {
-    if (buffer == NULL) return;
-
-    if (buffer->length + length > buffer->capacity) {
-        bh_buffer_grow(buffer, buffer->length + length);
-    }
-
-    memcpy(buffer->data + buffer->length, data, length);
-    buffer->length += length;
-}
-
-void bh_buffer_concat(bh_buffer* buffer, bh_buffer other) {
-    bh_buffer_append(buffer, other.data, other.length);
-}
-
-void bh_buffer_write_byte(bh_buffer* buffer, u8 byte) {
-    bh_buffer_grow(buffer, buffer->length + 1);
-    buffer->data[buffer->length++] = byte;
-}
-
-void bh_buffer_write_u32(bh_buffer* buffer, u32 i) {
-    bh_buffer_grow(buffer, buffer->length + 4);
-    *((u32 *) bh_pointer_add(buffer->data, buffer->length)) = i;
-    buffer->length += 4;
-}
-
-void bh_buffer_write_u64(bh_buffer* buffer, u64 i) {
-    bh_buffer_grow(buffer, buffer->length + 8);
-    *((u64 *) bh_pointer_add(buffer->data, buffer->length)) = i;
-    buffer->length += 8;
-}
-
-void bh_buffer_align(bh_buffer* buffer, u32 alignment) {
-    if (buffer->length % alignment != 0) {
-        u32 difference = alignment - (buffer->length % alignment);
-        buffer->length += difference;
-
-        bh_buffer_grow(buffer, buffer->length);
-    }
-}
-
-
-#endif
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// ARRAY IMPLEMENTATION
-//-------------------------------------------------------------------------------------
-#ifndef BH_NO_ARRAY
-
-b32 bh__arr_grow(bh_allocator alloc, void** arr, i32 elemsize, i32 cap) {
-    bh__arr* arrptr;
-
-    if (*arr == NULL) {
-        if (cap == 0 && elemsize == 0) return 1;
-
-        arrptr = (bh__arr *) bh_alloc(alloc, sizeof(*arrptr) + elemsize * cap);
-        if (arrptr == NULL) return 0;
-
-        arrptr->allocator = alloc;
-        arrptr->capacity = cap;
-        arrptr->length = 0;
-
-    } else {
-        arrptr = bh__arrhead(*arr);
-
-        if (arrptr->capacity < cap) {
-            void* p;
-            i32 newcap = arrptr->capacity;
-            while (newcap < cap) newcap = BH_ARR_GROW_FORMULA(newcap);
-
-            p = bh_resize(arrptr->allocator, arrptr, sizeof(*arrptr) + elemsize * newcap);
-
-            if (p) {
-                arrptr = (bh__arr *) p;
-                arrptr->capacity = newcap;
-            } else {
-                return 0;
-            }
-        }
-    }
-
-    *arr = arrptr + 1;
-    return 1;
-}
-
-b32 bh__arr_shrink(void** arr, i32 elemsize, i32 cap) {
-    if (*arr == NULL) return 0;
-
-    bh__arr* arrptr = bh__arrhead(*arr);
-    cap = bh_max(cap, arrptr->length);
-
-    if (arrptr->capacity > cap) {
-        void* p = bh_resize(arrptr->allocator, arrptr, sizeof(*arrptr) + elemsize * cap);
-
-        if (p) {
-            arrptr = (bh__arr *) p;
-            arrptr->capacity = cap;
-        } else {
-            return 0;
-        }
-    }
-
-    *arr = arrptr + 1;
-    return 1;
-}
-
-b32 bh__arr_free(void **arr) {
-    if (*arr == NULL) return 0;
-
-    bh__arr* arrptr = bh__arrhead(*arr);
-    bh_free(arrptr->allocator, arrptr);
-    *arr = NULL;
-    return 1;
-}
-
-void* bh__arr_copy(bh_allocator alloc, void *arr, i32 elemsize) {
-    bh__arr* arrptr = bh__arrhead(arr);
-
-    const i32 cap = arrptr->length;
-
-    void* newarr = NULL;
-    bh__arr_grow(alloc, &newarr, elemsize, cap);
-    bh__arrhead(newarr)->length = cap;
-    bh__arrhead(newarr)->capacity = cap;
-    memcpy(newarr, arr, elemsize * arrptr->length);
-
-    return newarr;
-}
-
-void bh__arr_deleten(void **arr, i32 elemsize, i32 index, i32 numelems) {
-    bh__arr* arrptr = bh__arrhead(*arr);
-
-    if (index >= arrptr->length) return; // Can't delete past the end of the array
-    if (numelems <= 0) return; // Can't delete nothing
-
-    memmove(
-        (char *)(*arr) + elemsize * index,                    // Target
-        (char *)(*arr) + elemsize * (index + numelems),        // Source
-        elemsize * (arrptr->length - (index + numelems)));    // Length
-    arrptr->length -= numelems;
-}
-
-void bh__arr_insertn(void **arr, i32 elemsize, i32 index, i32 numelems) {
-    if (numelems) {
-        if (*arr == NULL) {
-            bh__arr_grow(bh_arr_allocator(arr), arr, elemsize, numelems); // Making a new array
-            return;
-        }
-
-        bh__arr* arrptr = bh__arrhead(*arr);
-        if (!bh__arr_grow(bh_arr_allocator(arr), arr, elemsize, arrptr->length + numelems)) return; // Fail case
-        arrptr = bh__arrhead(*arr);
-        memmove(
-            (char *)(*arr) + elemsize * (index + numelems),
-            (char *)(*arr) + elemsize * index,
-            elemsize * (arrptr->length - index));
-        arrptr->length += numelems;
-    }
-}
-
-#endif // ifndef BH_NO_ARRAY
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// TABLE IMPLEMENTATION
-//-------------------------------------------------------------------------------------
-#ifndef BH_NO_TABLE
-
-b32 bh__table_init(bh_allocator allocator, bh__table **table, i32 table_size) {
-    *table = bh_alloc(allocator, sizeof(bh__table) + sizeof(ptr) * table_size);
-    if (*table == NULL) return 0;
-
-    (*table)->allocator = allocator;
-    (*table)->table_size = table_size;
-
-    for (i32 i = 0; i < table_size; i++) {
-        (*table)->arrs[i] = NULL;
-    }
-
-    return 1;
-}
-
-b32 bh__table_free(bh__table **table) {
-    if (*table == NULL) return 0;
-
-    for (u64 i = 0; i < (*table)->table_size; i++) {
-        if ((*table)->arrs[i] != NULL) {
-            bh_arr_free((*table)->arrs[i]);
-        }
-    }
-
-    bh_free((*table)->allocator, *table);
-    *table = NULL;
-    return 1;
-}
-
-// Assumes NULL terminated string for key
-ptr bh__table_put(bh__table *table, i32 elemsize, char *key) {
-    elemsize += (elemsize & 1);
-
-    u64 index = bh__table_hash_function(key, 0, table->table_size);
-    u8 arr_was_new = 0;
-
-    ptr arrptr = table->arrs[index];
-    if (arrptr == NULL) {
-        arr_was_new = 1;
-        goto add_new_element;
-    }
-    u64 len = *(u64 *) arrptr;
-    arrptr = bh_pointer_add(arrptr, sizeof(u64));
-
-    u16 key_length = 0;
-    while (len--) {
-        arrptr = bh_pointer_add(arrptr, elemsize);
-        key_length = *(u16 *) arrptr;
-        arrptr = bh_pointer_add(arrptr, sizeof(u16));
-        if (strncmp(key, (char *) arrptr, key_length) == 0) goto found_matching;
-        arrptr = bh_pointer_add(arrptr, key_length);
-    }
-
-add_new_element:
-    arrptr = table->arrs[index];
-    i32 byte_len = bh_arr_length(arrptr);
-    if (byte_len == 0) byte_len = sizeof(u64);
-    key_length = strlen(key) + 1;
-
-    // NOTE: Align to 16 bytes
-    if ((key_length + 2) % 16 != 0) {
-        key_length = ((((key_length + 2) >> 4) + 1) << 4) - 2;
-    }
-
-    bh__arr_grow(table->allocator, &arrptr, 1, byte_len + elemsize + sizeof(u16) + key_length);
-    bh__arrhead(arrptr)->length = byte_len + elemsize + sizeof(u16) + key_length;
-    table->arrs[index] = arrptr;
-
-    if (arr_was_new) {
-        *(u64 *) arrptr = 1;
-    } else {
-        (*(u64 *) arrptr)++;
-    }
-
-    arrptr = bh_pointer_add(arrptr, byte_len + elemsize);
-    *(u16 *) arrptr = key_length;
-    arrptr = bh_pointer_add(arrptr, sizeof(u16));
-    strncpy(arrptr, key, key_length);
-
-found_matching:
-    return bh_pointer_add(arrptr, -(sizeof(u16) + elemsize));
-}
-
-b32 bh__table_has(bh__table *table, i32 elemsize, char *key) {
-    elemsize += (elemsize & 1);
-
-    u64 index = bh__table_hash_function(key, 0, table->table_size);
-
-    ptr arrptr = table->arrs[index];
-    if (arrptr == NULL) return 0;
-
-    u64 len = *(u64 *) arrptr;
-    arrptr = bh_pointer_add(arrptr, sizeof(u64));
-
-    u16 key_length = 0;
-    while (len--) {
-        arrptr = bh_pointer_add(arrptr, elemsize);
-        key_length = *(u16 *) arrptr;
-        arrptr = bh_pointer_add(arrptr, sizeof(u16));
-        if (strncmp(key, (char *) arrptr, key_length) == 0) return 1;
-        arrptr = bh_pointer_add(arrptr, key_length);
-    }
-
-    return 0;
-}
-
-ptr bh__table_get(bh__table *table, i32 elemsize, char *key) {
-    elemsize += (elemsize & 1);
-
-    u64 index = bh__table_hash_function(key, 0, table->table_size);
-
-    ptr arrptr = table->arrs[index];
-    if (arrptr == NULL) return 0;
-
-    u64 len = *(u64 *) arrptr;
-    arrptr = bh_pointer_add(arrptr, sizeof(u64));
-
-    u16 key_length = 0;
-    while (len--) {
-        arrptr = bh_pointer_add(arrptr, elemsize);
-        key_length = *(u16 *) arrptr;
-        arrptr = bh_pointer_add(arrptr, sizeof(u16));
-        if (strncmp(key, (char *) arrptr, key_length) == 0) {
-            return bh_pointer_add(arrptr, -(sizeof(u16) + elemsize));
-        }
-        arrptr = bh_pointer_add(arrptr, key_length);
-    }
-
-    return NULL;
-}
-
-void bh__table_delete(bh__table *table, i32 elemsize, char *key) {
-    elemsize += (elemsize & 1);
-
-    u64 index = bh__table_hash_function(key, 0, table->table_size);
-
-    ptr arrptr = table->arrs[index], walker;
-    if (arrptr == NULL) return; // Didn't exist
-    walker = arrptr;
-
-    i32 byte_offset = 8;
-    i32 delete_len = 0;
-
-    u64 len = *(u64 *) walker;
-    walker = bh_pointer_add(walker, sizeof(u64));
-
-    u16 key_length = 0;
-    while (len--) {
-        walker = bh_pointer_add(walker, elemsize);
-        key_length = *(u16 *) walker;
-        walker = bh_pointer_add(walker, sizeof(u16));
-        if (strncmp(key, (char *) walker, key_length) == 0) {
-            delete_len = elemsize + sizeof(u16) + key_length;
-            goto found_matching;
-        }
-        walker = bh_pointer_add(walker, key_length);
-        byte_offset += elemsize + sizeof(u16) + key_length;
-    }
-
-    // NOTE: Already didn't exist
-    return;
-
-found_matching:
-    bh__arr_deleten((void **) &arrptr, 1, byte_offset, delete_len);
-    table->arrs[index] = arrptr;
-    (*(u64 *) arrptr)--;
-}
-
-void bh__table_clear(bh__table *table) {
-    for (u64 i = 0; i < table->table_size; i++) {
-        if (table->arrs[i] != NULL) {
-            // NOTE: Set length property to 0
-            *((u64 *) table->arrs[i]) = 0;
-            bh_arr_set_length(table->arrs[i], 0);
-        }
-    }
-}
-
-bh_table_iterator bh__table_iter_setup(bh__table *table, i32 elemsize) {
-    elemsize += (elemsize & 1);
-
-    bh_table_iterator it = {
-        .tab = table->arrs,
-        .endtab = table->arrs + table->table_size,
-        .elemsize = elemsize,
-        .entry = NULL
-    };
-    return it;
-}
-
-b32 bh_table_iter_next(bh_table_iterator* it) {
-    if (it->tab == NULL) return 0;
-
-    if (it->entry != NULL) {
-        it->arrlen--;
-        if (it->arrlen <= 0) {
-            it->tab++;
-            goto step_to_next;
-        }
-
-        it->entry = bh_pointer_add(it->entry, it->elemsize);
-        it->entry = bh_pointer_add(it->entry, sizeof(u16) + (*(u16 *) it->entry));
-        return 1;
-    }
-
-step_to_next:
-    // Step forward to find next valid
-    while (it->tab != it->endtab && *it->tab == NULL) {
-        it->tab++;
-    }
-
-    if (it->tab == it->endtab) return 0;
-
-    it->entry = *it->tab;
-    it->arrlen = *(u64 *) it->entry;
-    it->entry = bh_pointer_add(it->entry, sizeof(u64));
-    if (it->arrlen <= 0) {
-        it->tab++;
-        goto step_to_next;
-    }
-    return 1;
-}
-
-#endif // ifndef BH_NO_HASHTABLE
-
-
-
-//-------------------------------------------------------------------------------------
-// IMAP IMPLEMENTATION
-//-------------------------------------------------------------------------------------
-#ifndef BH_NO_IMAP
-void bh_imap_init(bh_imap* imap, bh_allocator alloc, i32 hash_count) {
-    imap->allocator = alloc;
-
-    imap->hashes  = NULL;
-    imap->entries = NULL;
-
-    bh_arr_new(alloc, imap->hashes, hash_count);
-    bh_arr_new(alloc, imap->entries, 4);
-
-    fori(count, 0, hash_count) bh_arr_push(imap->hashes, -1);
-}
-
-void bh_imap_free(bh_imap* imap) {
-    bh_arr_free(imap->hashes);
-    bh_arr_free(imap->entries);
-
-    imap->hashes = NULL;
-    imap->entries = NULL;
-}
-
-bh__imap_lookup_result bh__imap_lookup(bh_imap* imap, bh_imap_entry_t key) {
-    bh__imap_lookup_result lr = { -1, -1, -1 };
-
-    u64 hash = 0xcbf29ce484222325ull ^ key;
-    u64 n = bh_arr_capacity(imap->hashes);
-
-    lr.hash_index  = hash % n;
-    lr.entry_index = imap->hashes[lr.hash_index];
-    while (lr.entry_index >= 0) {
-        if (imap->entries[lr.entry_index].key == key) {
-            return lr;
-        }
-
-        lr.entry_prev  = lr.entry_index;
-        lr.entry_index = imap->entries[lr.entry_index].next;
-    }
-
-    return lr;
-}
-
-void bh_imap_put(bh_imap* imap, bh_imap_entry_t key, bh_imap_entry_t value) {
-    bh__imap_lookup_result lr = bh__imap_lookup(imap, key);
-
-    if (lr.entry_index >= 0) {
-        imap->entries[lr.entry_index].value = value;
-        return;
-    }
-
-    bh__imap_entry entry;
-    entry.key = key;
-    entry.value = value;
-    entry.next = imap->hashes[lr.hash_index];
-    bh_arr_push(imap->entries, entry);
-
-    imap->hashes[lr.hash_index] = bh_arr_length(imap->entries) - 1;
-}
-
-b32 bh_imap_has(bh_imap* imap, bh_imap_entry_t key) {
-    bh__imap_lookup_result lr = bh__imap_lookup(imap, key);
-    return lr.entry_index >= 0;
-}
-
-bh_imap_entry_t bh_imap_get(bh_imap* imap, bh_imap_entry_t key) {
-    bh__imap_lookup_result lr = bh__imap_lookup(imap, key);
-    if (lr.entry_index >= 0) {
-        return imap->entries[lr.entry_index].value;
-    } else {
-        return 0;
-    }
-}
-
-void bh_imap_delete(bh_imap* imap, bh_imap_entry_t key) {
-    bh__imap_lookup_result lr = bh__imap_lookup(imap, key);
-    if (lr.entry_index < 0) return;
-
-    if (lr.entry_prev < 0) {
-        imap->hashes[lr.hash_index] = imap->entries[lr.entry_index].next;
-    } else {
-        imap->entries[lr.entry_prev].next = imap->entries[lr.entry_index].next;
-    }
-
-    // If it's that last thing in the array, just pop off the end
-    if (lr.entry_index == bh_arr_length(imap->entries) - 1) {
-        bh_arr_pop(imap->entries);
-        return;
-    }
-
-    bh_arr_fastdelete(imap->entries, lr.entry_index);
-    bh__imap_lookup_result last = bh__imap_lookup(imap, imap->entries[lr.entry_index].key);
-    if (last.entry_prev >= 0) {
-        imap->entries[last.entry_prev].next = lr.entry_index;
-    } else {
-        imap->hashes[last.hash_index] = lr.entry_index;
-    }
-}
-
-void bh_imap_clear(bh_imap* imap) {
-    // NOTE: Does not clear out an of the data that was in the map
-    bh_arr_each(i64, hash, imap->hashes) *hash = -1;
-    bh_arr_set_length(imap->entries, 0);
-}
-
-#endif // ifndef BH_NO_IMAP
-
-
-
-
-
-u64 bh_time_curr() {
-#if defined(_BH_WINDOWS)
-    LARGE_INTEGER result;
-    QueryPerformanceCounter(&result);
-    return (u64) result.QuadPart;
-
-#elif defined(_BH_LINUX)
-    struct timespec spec;
-    clock_gettime(CLOCK_REALTIME, &spec);
-
-    time_t sec = spec.tv_sec;
-    u64 ms  = spec.tv_nsec / 1000000;
-    if (ms > 999) {
-        sec++;
-        ms = 0;
-    }
-
-    return sec * 1000 + ms;
-#endif
-}
-
-u64 bh_time_duration(u64 old) {
-#if defined(_BH_WINDOWS)
-    u64 curr = bh_time_curr();
-    u64 duration = curr - old;
-    
-    LARGE_INTEGER freq;
-    QueryPerformanceFrequency(&freq);
-    duration *= 1000;
-    duration /= freq.QuadPart;
-    return duration;
-
-#elif defined(_BH_LINUX)
-    u64 curr = bh_time_curr();
-    return curr - old;
-#endif
-}
-
-#endif // ifdef BH_DEFINE
-
-#endif // ifndef BH_H
diff --git a/include/doc.h b/include/doc.h
deleted file mode 100644 (file)
index 75d4bfc..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-#ifndef ONYXDOC_H
-#define ONYXDOC_H
-
-#include "bh.h"
-#include "astnodes.h"
-
-typedef enum DocFormat {
-    Doc_Format_Human,
-    Doc_Format_Tags,
-    Doc_Format_Html,
-} DocFormat;
-
-typedef struct DocEntry {
-       OnyxFilePos pos;
-    char* sym; // Unused by doc generator
-       char* def;
-       char* additional;
-} DocEntry;
-
-typedef struct DocPackage {
-       const char* name;
-
-       bh_arr(DocEntry) public_entries;
-       bh_arr(DocEntry) private_entries;
-} DocPackage;
-
-typedef struct OnyxDocumentation {
-       bh_arena doc_arena;
-
-    DocFormat format;
-
-       bh_arr(DocPackage) package_docs;
-} OnyxDocumentation;
-
-OnyxDocumentation onyx_docs_generate();
-void onyx_docs_emit(OnyxDocumentation* doc, const char* filename);
-
-#endif
diff --git a/include/errors.h b/include/errors.h
deleted file mode 100644 (file)
index 9df95de..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-#ifndef ONYXERRORS_H
-#define ONYXERRORS_H
-
-#include "bh.h"
-#include "lex.h"
-
-#include <stdarg.h>
-
-typedef enum OnyxErrorRank {
-    Error_Undefined = 0,
-    Error_Other = 1,
-    Error_Warning = 2,
-    Error_Waiting_On = 3,
-    Error_Critical = 4,
-} OnyxErrorRank;
-
-typedef struct OnyxError {
-    OnyxFilePos pos;
-    OnyxErrorRank rank;
-    char *text;
-} OnyxError;
-
-typedef struct OnyxErrors {
-    bh_arena     msg_arena;
-    bh_allocator msg_alloc;
-
-    // NOTE: Pointer to a array of all the loaded files.
-    bh_arr(bh_file_contents)* file_contents;
-
-    bh_arr(OnyxError) errors;
-} OnyxErrors;
-
-extern OnyxErrors msgs;
-
-void onyx_errors_init(bh_arr(bh_file_contents)* files);
-void onyx_submit_error(OnyxError error);
-void onyx_report_error(OnyxFilePos pos, OnyxErrorRank rank, char * format, ...);
-void onyx_submit_warning(OnyxError error);
-void onyx_report_warning(OnyxFilePos pos, char* format, ...);
-void onyx_errors_print();
-b32  onyx_has_errors();
-void onyx_clear_errors();
-
-#endif
diff --git a/include/lex.h b/include/lex.h
deleted file mode 100644 (file)
index df68447..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-#ifndef ONYXLEX_H
-#define ONYXLEX_H
-
-#include "bh.h"
-
-// NOTE: Used for global statistics
-extern u64 lexer_lines_processed;
-extern u64 lexer_tokens_processed;
-
-typedef enum TokenType {
-    Token_Type_Ascii_End            = 256,
-    Token_Type_Unknown              = 256,
-    Token_Type_End_Stream           = 257,
-
-    Token_Type_Comment              = 258,
-
-    Token_Type_Keyword_Package,
-    Token_Type_Keyword_Struct,
-    Token_Type_Keyword_Enum,
-    Token_Type_Keyword_Use,
-    Token_Type_Keyword_If,
-    Token_Type_Keyword_Else,
-    Token_Type_Keyword_Elseif,
-    Token_Type_Keyword_Return,
-    Token_Type_Keyword_Global,
-    Token_Type_Keyword_Cast,
-    Token_Type_Keyword_While,
-    Token_Type_Keyword_For,
-    Token_Type_Keyword_Break,
-    Token_Type_Keyword_Continue,
-    Token_Type_Keyword_Sizeof,
-    Token_Type_Keyword_Alignof,
-    Token_Type_Keyword_Typeof,
-    Token_Type_Keyword_Defer,
-    Token_Type_Keyword_Do,
-    Token_Type_Keyword_Case,
-    Token_Type_Keyword_Switch,
-    Token_Type_Keyword_Fallthrough,
-    Token_Type_Keyword_Macro,
-    Token_Type_Keyword_Interface,
-    Token_Type_Keyword_Where,
-
-    Token_Type_Right_Arrow,
-    Token_Type_Left_Arrow,
-    Token_Type_Empty_Block,
-    Token_Type_Pipe,
-
-    Token_Type_Greater_Equal,
-    Token_Type_Less_Equal,
-    Token_Type_Equal_Equal,
-    Token_Type_Not_Equal,
-    Token_Type_Plus_Equal,
-    Token_Type_Minus_Equal,
-    Token_Type_Star_Equal,
-    Token_Type_Fslash_Equal,
-    Token_Type_Percent_Equal,
-    Token_Type_And_Equal,
-    Token_Type_Or_Equal,
-    Token_Type_Xor_Equal,
-    Token_Type_And_And,
-    Token_Type_Or_Or,
-    Token_Type_Shift_Left,
-    Token_Type_Shift_Right,
-    Token_Type_Shift_Arith_Right,
-    Token_Type_Shl_Equal,
-    Token_Type_Shr_Equal,
-    Token_Type_Sar_Equal,
-
-    Token_Type_Dot_Dot,
-    Token_Type_Tilde_Tilde,
-
-    Token_Type_Symbol,
-    Token_Type_Literal_String,
-    Token_Type_Literal_Integer,
-    Token_Type_Literal_Float,
-    Token_Type_Literal_True,
-    Token_Type_Literal_False,
-
-    Token_Type_Note,
-
-    Token_Type_Count,
-} TokenType;
-
-typedef struct OnyxFilePos {
-    const char* filename;
-    char* line_start;
-    u32 line;
-
-    // NOTE: This assumes that no line is no longer than 2^16 chars
-    u16 column, length;
-} OnyxFilePos;
-
-typedef struct OnyxToken {
-    TokenType type;
-    i32 length;
-    char* text;
-    OnyxFilePos pos;
-} OnyxToken;
-
-typedef struct OnyxTokenizer {
-    char *start, *curr, *end;
-
-    const char* filename;
-
-    char* line_start;
-    u64 line_number;
-
-    bh_arr(OnyxToken) tokens;
-} OnyxTokenizer;
-
-const char* token_name(TokenType tkn_type);
-void token_toggle_end(OnyxToken* tkn);
-OnyxToken* onyx_get_token(OnyxTokenizer* tokenizer);
-OnyxTokenizer onyx_tokenizer_create(bh_allocator allocator, bh_file_contents *fc);
-void onyx_tokenizer_free(OnyxTokenizer* tokenizer);
-void onyx_lex_tokens(OnyxTokenizer* tokenizer);
-
-b32 token_equals(OnyxToken* tkn1, OnyxToken* tkn2);
-b32 token_text_equals(OnyxToken* tkn, char* text);
-b32 token_same_file(OnyxToken *tkn1, OnyxToken *tkn2);
-
-#endif
diff --git a/include/onyx_library.h b/include/onyx_library.h
deleted file mode 100644 (file)
index 573de30..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-
-#include "wasm.h"
-
-#if defined(_WIN32) || defined(_WIN64)
-    #define ONYX_EXPORT extern __declspec(dllexport)
-    #define ONYX_IMPORT extern __declspec(dllimport)
-#endif
-
-#if defined(__unix__)
-    #define ONYX_EXPORT
-    #define ONYX_IMPORT
-#endif
-
-typedef struct OnyxRuntime {
-    wasm_instance_t* wasm_instance;
-    wasm_module_t* wasm_module;
-    wasm_memory_t* wasm_memory;
-    wasm_engine_t *wasm_engine;
-    wasm_extern_vec_t wasm_imports;
-
-    int argc;
-    char **argv;
-
-    // HACK HACK HACK
-    // There should need to be this much stuff in here, but because Wasmer doesn't ship a "wasmerdll.lib"
-    // file for windows, it is impossible for it to link successfully against the function provided in onyx.exe.
-    // Therefore, we must serve as the linker and do this manually. Hopefully that library file will be
-    // shipped soon so this can go away...
-    char* (*wasm_memory_data)(wasm_memory_t *wasm_memory);
-    wasm_extern_t* (*wasm_extern_lookup_by_name)(wasm_module_t* module, wasm_instance_t* instance, const char* name);
-    wasm_func_t* (*wasm_extern_as_func)(wasm_extern_t* ext);
-    wasm_trap_t* (*wasm_func_call)(const wasm_func_t* wasm_func, const wasm_val_vec_t* args, wasm_val_vec_t* results);
-    wasm_instance_t* (*wasm_instance_new)(wasm_store_t* store, const wasm_module_t* module, const wasm_extern_vec_t* imports, wasm_trap_t** traps);
-    void (*onyx_print_trap)(wasm_trap_t* trap);
-    wasm_store_t *(*wasm_store_new)(wasm_engine_t *engine);
-    void (*wasm_store_delete)(wasm_store_t *store);
-} OnyxRuntime;
-
-OnyxRuntime* runtime;
-
-typedef struct WasmValkindBuffer {
-    unsigned int count;
-    wasm_valkind_t types[20];
-} WasmValkindBuffer;
-
-typedef struct WasmFuncDefinition {
-    char* module_name;
-    char* import_name;
-    wasm_func_callback_t func;
-
-    WasmValkindBuffer *params;
-    WasmValkindBuffer *results;
-} WasmFuncDefinition;
-
-#define STRINGIFY1(a) #a
-#define STRINGIFY2(a) STRINGIFY1(a)
-#define CONCAT2(a, b) a ## _ ## b
-#define CONCAT3(a, b, c) a ## _ ## b ## _ ## c
-#define ONYX_MODULE_NAME_GEN(m) CONCAT2(__onyx_library, m)
-#define ONYX_LINK_NAME_GEN(m) CONCAT2(onyx_library, m)
-#define ONYX_FUNC_NAME(m, n) CONCAT3(__onyx_internal, m, n)
-#define ONYX_DEF_NAME(m, n) CONCAT3(__onyx_internal_def, m, n)
-#define ONYX_PARAM_NAME(m, n) CONCAT3(__onyx_internal_param_buffer, m, n)
-#define ONYX_RESULT_NAME(m, n) CONCAT3(__onyx_internal_result_buffer, m, n)
-#define ONYX_IMPORT_NAME(m, n) STRINGIFY1(m) "_" #n
-
-#define NUM_VALS(...) (sizeof((wasm_valkind_t []){ 0, __VA_ARGS__ }) / sizeof(wasm_valkind_t))
-#define _VALS(...) { NUM_VALS(__VA_ARGS__) - 1, __VA_ARGS__ }
-
-#define ONYX_DEF(name, params_types, result_types) \
-    static wasm_trap_t* ONYX_FUNC_NAME(ONYX_LIBRARY_NAME, name)(const wasm_val_vec_t* params, wasm_val_vec_t* results); \
-    static struct WasmValkindBuffer  ONYX_PARAM_NAME(ONYX_LIBRARY_NAME, name) = _VALS params_types; \
-    static struct WasmValkindBuffer  ONYX_RESULT_NAME(ONYX_LIBRARY_NAME, name) = _VALS result_types; \
-    static struct WasmFuncDefinition ONYX_DEF_NAME(ONYX_LIBRARY_NAME, name) = { STRINGIFY2(ONYX_LIBRARY_NAME), #name, ONYX_FUNC_NAME(ONYX_LIBRARY_NAME, name), & ONYX_PARAM_NAME(ONYX_LIBRARY_NAME, name), & ONYX_RESULT_NAME(ONYX_LIBRARY_NAME, name) }; \
-    \
-    static wasm_trap_t* ONYX_FUNC_NAME(ONYX_LIBRARY_NAME, name)(const wasm_val_vec_t* params, wasm_val_vec_t* results)
-
-#define ONYX_FUNC(name) & ONYX_DEF_NAME(ONYX_LIBRARY_NAME, name),
-#define ONYX_LIBRARY \
-    extern struct WasmFuncDefinition *ONYX_MODULE_NAME_GEN(ONYX_LIBRARY_NAME)[]; \
-    ONYX_EXPORT WasmFuncDefinition** ONYX_LINK_NAME_GEN(ONYX_LIBRARY_NAME)(OnyxRuntime* in_runtime) { \
-        runtime = in_runtime; \
-        return ONYX_MODULE_NAME_GEN(ONYX_LIBRARY_NAME); \
-    } \
-    struct WasmFuncDefinition *ONYX_MODULE_NAME_GEN(ONYX_LIBRARY_NAME)[] =
-
-// Shorter names
-#ifndef ONYX_NO_SHORT_NAMES
-#undef  BOOL
-#undef  INT
-#undef  LONG
-#undef  FLOAT
-#undef  DOUBLE
-#define BOOL WASM_I32
-#define INT WASM_I32
-#define LONG WASM_I64
-#define FLOAT WASM_F32
-#define DOUBLE WASM_F64
-#define PTR WASM_I32
-#endif
-
-#define ONYX_PTR(p) ((void*) (p != 0 ? (runtime->wasm_memory_data(runtime->wasm_memory) + p) : NULL))
diff --git a/include/parser.h b/include/parser.h
deleted file mode 100644 (file)
index 48889a8..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-#ifndef ONYXPARSER_H
-#define ONYXPARSER_H
-
-#include "bh.h"
-
-#include "lex.h"
-#include "errors.h"
-#include "astnodes.h"
-
-typedef struct PolymorphicContext {
-    AstType* root_node;
-    bh_arr(AstPolyParam)* poly_params;
-} PolymorphicContext;
-
-typedef struct OnyxParser {
-    bh_allocator allocator;
-
-    Package *package;
-    Scope *file_scope;
-
-    // NOTE: not used since all tokens are lexed before parsing starts
-    OnyxTokenizer *tokenizer;
-    OnyxToken *prev;
-    OnyxToken *curr;
-
-    PolymorphicContext polymorph_context;
-
-    bh_arr(AstFunction *) current_function_stack;
-    Scope *current_scope;
-    bh_arr(bh_arr(Entity *) *) alternate_entity_placement_stack;
-    bh_arr(OnyxToken *) current_symbol_stack;
-
-    bh_arr(AstFlags) scope_flags;
-
-    bh_arr(AstTyped *) stored_tags;
-
-    u16 tag_depth : 16;
-
-    b32 hit_unexpected_token : 1;
-    b32 parse_calls : 1;
-} OnyxParser;
-
-const char* onyx_ast_node_kind_string(AstKind kind);
-void* onyx_ast_node_new(bh_allocator alloc, i32 size, AstKind kind);
-OnyxParser onyx_parser_create(bh_allocator alloc, OnyxTokenizer *tokenizer);
-void onyx_parser_free(OnyxParser* parser);
-void onyx_parse(OnyxParser *parser);
-
-#endif // #ifndef ONYXPARSER_H
diff --git a/include/small_windows.h b/include/small_windows.h
deleted file mode 100644 (file)
index 28cea0b..0000000
+++ /dev/null
@@ -1,444 +0,0 @@
-// Bill's Mini Windows.h from https://github.com/odin-lang/Odin/blob/master/src/gb/gb.h
-
-////////////////////////////////////////////////////////////////
-//
-// Bill's Mini Windows.h
-//
-//
-
-#define GB_EXTERN extern
-#define GB_DLL_EXPORT GB_EXTERN __declspec(dllexport)
-#define GB_DLL_IMPORT GB_EXTERN __declspec(dllimport)
-
-#define WINAPI   __stdcall
-#define WINAPIV  __cdecl
-#define CALLBACK __stdcall
-#define MAX_PATH 260
-#define CCHDEVICENAME 32
-#define CCHFORMNAME   32
-
-typedef unsigned long DWORD;
-typedef int WINBOOL;
-#ifndef XFree86Server
-    #ifndef __OBJC__
-    typedef WINBOOL BOOL;
-    #else
-    #define BOOL WINBOOL
-    #endif
-typedef unsigned char BYTE;
-#endif
-typedef unsigned short WORD;
-typedef float FLOAT;
-typedef int INT;
-typedef unsigned int UINT;
-typedef short SHORT;
-typedef long LONG;
-typedef long long LONGLONG;
-typedef unsigned short USHORT;
-typedef unsigned long ULONG;
-typedef unsigned long long ULONGLONG;
-
-typedef UINT WPARAM;
-typedef LONG LPARAM;
-typedef LONG LRESULT;
-#ifndef _HRESULT_DEFINED
-typedef LONG HRESULT;
-#define _HRESULT_DEFINED
-#endif
-#ifndef XFree86Server
-typedef WORD ATOM;
-#endif /* XFree86Server */
-typedef void *HANDLE;
-typedef HANDLE HGLOBAL;
-typedef HANDLE HLOCAL;
-typedef HANDLE GLOBALHANDLE;
-typedef HANDLE LOCALHANDLE;
-typedef void *HGDIOBJ;
-
-#define DECLARE_HANDLE(name) typedef HANDLE name
-DECLARE_HANDLE(HACCEL);
-DECLARE_HANDLE(HBITMAP);
-DECLARE_HANDLE(HBRUSH);
-DECLARE_HANDLE(HCOLORSPACE);
-DECLARE_HANDLE(HDC);
-DECLARE_HANDLE(HGLRC);
-DECLARE_HANDLE(HDESK);
-DECLARE_HANDLE(HENHMETAFILE);
-DECLARE_HANDLE(HFONT);
-DECLARE_HANDLE(HICON);
-DECLARE_HANDLE(HKEY);
-typedef HKEY *PHKEY;
-DECLARE_HANDLE(HMENU);
-DECLARE_HANDLE(HMETAFILE);
-DECLARE_HANDLE(HINSTANCE);
-typedef HINSTANCE HMODULE;
-DECLARE_HANDLE(HPALETTE);
-DECLARE_HANDLE(HPEN);
-DECLARE_HANDLE(HRGN);
-DECLARE_HANDLE(HRSRC);
-DECLARE_HANDLE(HSTR);
-DECLARE_HANDLE(HTASK);
-DECLARE_HANDLE(HWND);
-DECLARE_HANDLE(HWINSTA);
-DECLARE_HANDLE(HKL);
-DECLARE_HANDLE(HRAWINPUT);
-DECLARE_HANDLE(HMONITOR);
-#undef DECLARE_HANDLE
-
-typedef int HFILE;
-typedef HICON HCURSOR;
-typedef DWORD COLORREF;
-typedef int (WINAPI *FARPROC)();
-typedef int (WINAPI *NEARPROC)();
-typedef int (WINAPI *PROC)();
-typedef LRESULT (CALLBACK *WNDPROC)(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
-
-#if defined(_WIN64)
-typedef unsigned __int64 ULONG_PTR;
-typedef signed __int64 LONG_PTR;
-#else
-typedef unsigned long ULONG_PTR;
-typedef signed long LONG_PTR;
-#endif
-typedef ULONG_PTR DWORD_PTR;
-
-typedef struct tagRECT {
-    LONG left;
-    LONG top;
-    LONG right;
-    LONG bottom;
-} RECT;
-typedef struct tagRECTL {
-    LONG left;
-    LONG top;
-    LONG right;
-    LONG bottom;
-} RECTL;
-typedef struct tagPOINT {
-    LONG x;
-    LONG y;
-} POINT;
-typedef struct tagSIZE {
-    LONG cx;
-    LONG cy;
-} SIZE;
-typedef struct tagPOINTS {
-    SHORT x;
-    SHORT y;
-} POINTS;
-typedef struct _SECURITY_ATTRIBUTES {
-    DWORD  nLength;
-    HANDLE lpSecurityDescriptor;
-    BOOL   bInheritHandle;
-} SECURITY_ATTRIBUTES;
-typedef enum _LOGICAL_PROCESSOR_RELATIONSHIP {
-    RelationProcessorCore,
-    RelationNumaNode,
-    RelationCache,
-    RelationProcessorPackage,
-    RelationGroup,
-    RelationAll               = 0xffff
-} LOGICAL_PROCESSOR_RELATIONSHIP;
-typedef enum _PROCESSOR_CACHE_TYPE {
-    CacheUnified,
-    CacheInstruction,
-    CacheData,
-    CacheTrace
-} PROCESSOR_CACHE_TYPE;
-typedef struct _CACHE_DESCRIPTOR {
-    BYTE                 Level;
-    BYTE                 Associativity;
-    WORD                 LineSize;
-    DWORD                Size;
-    PROCESSOR_CACHE_TYPE Type;
-} CACHE_DESCRIPTOR;
-typedef struct _SYSTEM_LOGICAL_PROCESSOR_INFORMATION {
-    ULONG_PTR                       ProcessorMask;
-    LOGICAL_PROCESSOR_RELATIONSHIP Relationship;
-    union {
-        struct {
-            BYTE Flags;
-        } ProcessorCore;
-        struct {
-            DWORD NodeNumber;
-        } NumaNode;
-        CACHE_DESCRIPTOR Cache;
-        ULONGLONG        Reserved[2];
-    };
-} SYSTEM_LOGICAL_PROCESSOR_INFORMATION;
-typedef struct _MEMORY_BASIC_INFORMATION {
-    void *BaseAddress;
-    void *AllocationBase;
-    DWORD AllocationProtect;
-    size_t RegionSize;
-    DWORD State;
-    DWORD Protect;
-    DWORD Type;
-} MEMORY_BASIC_INFORMATION;
-typedef struct _SYSTEM_INFO {
-    union {
-        DWORD   dwOemId;
-        struct {
-            WORD wProcessorArchitecture;
-            WORD wReserved;
-        };
-    };
-    DWORD     dwPageSize;
-    void *    lpMinimumApplicationAddress;
-    void *    lpMaximumApplicationAddress;
-    DWORD_PTR dwActiveProcessorMask;
-    DWORD     dwNumberOfProcessors;
-    DWORD     dwProcessorType;
-    DWORD     dwAllocationGranularity;
-    WORD      wProcessorLevel;
-    WORD      wProcessorRevision;
-} SYSTEM_INFO;
-typedef union _LARGE_INTEGER {
-    struct {
-        DWORD LowPart;
-        LONG  HighPart;
-    };
-    struct {
-        DWORD LowPart;
-        LONG  HighPart;
-    } u;
-    LONGLONG QuadPart;
-} LARGE_INTEGER;
-typedef union _ULARGE_INTEGER {
-    struct {
-        DWORD LowPart;
-        DWORD HighPart;
-    };
-    struct {
-        DWORD LowPart;
-        DWORD HighPart;
-    } u;
-    ULONGLONG QuadPart;
-} ULARGE_INTEGER;
-
-typedef struct _OVERLAPPED {
-    ULONG_PTR Internal;
-    ULONG_PTR InternalHigh;
-    union {
-        struct {
-            DWORD Offset;
-            DWORD OffsetHigh;
-        };
-        void *Pointer;
-    };
-    HANDLE hEvent;
-} OVERLAPPED;
-typedef struct _FILETIME {
-    DWORD dwLowDateTime;
-    DWORD dwHighDateTime;
-} FILETIME;
-typedef struct _WIN32_FIND_DATAW {
-    DWORD    dwFileAttributes;
-    FILETIME ftCreationTime;
-    FILETIME ftLastAccessTime;
-    FILETIME ftLastWriteTime;
-    DWORD    nFileSizeHigh;
-    DWORD    nFileSizeLow;
-    DWORD    dwReserved0;
-    DWORD    dwReserved1;
-    wchar_t  cFileName[MAX_PATH];
-    wchar_t  cAlternateFileName[14];
-} WIN32_FIND_DATAW;
-typedef struct _WIN32_FIND_DATAA {
-    DWORD    dwFileAttributes;
-    FILETIME ftCreationTime;
-    FILETIME ftLastAccessTime;
-    FILETIME ftLastWriteTime;
-    DWORD    nFileSizeHigh;
-    DWORD    nFileSizeLow;
-    DWORD    dwReserved0;
-    DWORD    dwReserved1;
-    char  cFileName[MAX_PATH];
-    char  cAlternateFileName[14];
-} WIN32_FIND_DATAA;
-typedef struct _WIN32_FILE_ATTRIBUTE_DATA {
-    DWORD    dwFileAttributes;
-    FILETIME ftCreationTime;
-    FILETIME ftLastAccessTime;
-    FILETIME ftLastWriteTime;
-    DWORD    nFileSizeHigh;
-    DWORD    nFileSizeLow;
-} WIN32_FILE_ATTRIBUTE_DATA;
-typedef enum _GET_FILEEX_INFO_LEVELS {
-    GetFileExInfoStandard,
-    GetFileExMaxInfoLevel
-} GET_FILEEX_INFO_LEVELS;
-typedef struct _STARTUPINFOA {
-  DWORD  cb;
-  char * lpReserved;
-  char * lpDesktop;
-  char * lpTitle;
-  DWORD  dwX;
-  DWORD  dwY;
-  DWORD  dwXSize;
-  DWORD  dwYSize;
-  DWORD  dwXCountChars;
-  DWORD  dwYCountChars;
-  DWORD  dwFillAttribute;
-  DWORD  dwFlags;
-  WORD   wShowWindow;
-  WORD   cbReserved2;
-  char * lpReserved2;
-  HANDLE hStdInput;
-  HANDLE hStdOutput;
-  HANDLE hStdError;
-} STARTUPINFOA, *LPSTARTUPINFOA;
-typedef struct _PROCESS_INFORMATION {
-  HANDLE hProcess;
-  HANDLE hThread;
-  DWORD  dwProcessId;
-  DWORD  dwThreadId;
-} PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;
-
-#define INFINITE 0xffffffffl
-#define INVALID_HANDLE_VALUE ((void *)(intptr_t)(-1))
-#define STARTF_USESTDHANDLES 0x00000100
-
-typedef DWORD WINAPI THREAD_START_ROUTINE(void *parameter);
-
-GB_DLL_IMPORT DWORD   WINAPI GetLastError       (void);
-GB_DLL_IMPORT BOOL    WINAPI CloseHandle        (HANDLE object);
-GB_DLL_IMPORT HANDLE  WINAPI CreateSemaphoreA   (SECURITY_ATTRIBUTES *semaphore_attributes, LONG initial_count,
-                                                 LONG maximum_count, char const *name);
-GB_DLL_IMPORT BOOL    WINAPI ReleaseSemaphore   (HANDLE semaphore, LONG release_count, LONG *previous_count);
-GB_DLL_IMPORT DWORD   WINAPI WaitForSingleObject(HANDLE handle, DWORD milliseconds);
-GB_DLL_IMPORT HANDLE  WINAPI CreateThread       (SECURITY_ATTRIBUTES *semaphore_attributes, size_t stack_size,
-                                                 THREAD_START_ROUTINE *start_address, void *parameter,
-                                                 DWORD creation_flags, DWORD *thread_id);
-GB_DLL_IMPORT DWORD   WINAPI GetThreadId        (HANDLE handle);
-GB_DLL_IMPORT void    WINAPI RaiseException     (DWORD, DWORD, DWORD, ULONG_PTR const *);
-GB_DLL_IMPORT BOOL    WINAPI TerminateThread    (HANDLE hThread, DWORD dwExitCode);
-GB_DLL_IMPORT BOOL    WINAPI CreateProcessA     (char const * lpApplicationName, char * lpCommandLine,
-                                                 SECURITY_ATTRIBUTES* lpProcessAttrs, SECURITY_ATTRIBUTES* lpThreadAttributes,
-                                                 BOOL bInheritHandles, DWORD dwCreationFlags, void* lpEnvironment,
-                                                 char const * lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo,
-                                                 LPPROCESS_INFORMATION lpProcessInformation);
-GB_DLL_IMPORT BOOL    WINAPI GetExitCodeProcess (HANDLE hProcess, DWORD *lpExitCode);
-GB_DLL_IMPORT BOOL    WINAPI CreatePipe         (HANDLE *hReadPipe, HANDLE *hWritePipe, SECURITY_ATTRIBUTES* lpPipeAttributes,
-                                                 DWORD nSize);
-GB_DLL_IMPORT BOOL    WINAPI TerminateProcess   (HANDLE hProcess, UINT uExitCode);
-GB_DLL_IMPORT BOOL    WINAPI SetHandleInformation(HANDLE hObject, DWORD dwMask, DWORD dwFlags);
-
-uintptr_t _beginthreadex( // NATIVE CODE
-   void *security,
-   unsigned stack_size,
-   unsigned ( __stdcall *start_address )( void * ),
-   void *arglist,
-   unsigned initflag,
-   unsigned *thrdaddr
-);
-
-
-GB_DLL_IMPORT BOOL      WINAPI GetLogicalProcessorInformation(SYSTEM_LOGICAL_PROCESSOR_INFORMATION *buffer, DWORD *return_length);
-GB_DLL_IMPORT DWORD_PTR WINAPI SetThreadAffinityMask(HANDLE thread, DWORD_PTR check_mask);
-GB_DLL_IMPORT HANDLE    WINAPI GetCurrentThread(void);
-
-#define PAGE_NOACCESS          0x01
-#define PAGE_READONLY          0x02
-#define PAGE_READWRITE         0x04
-#define PAGE_WRITECOPY         0x08
-#define PAGE_EXECUTE           0x10
-#define PAGE_EXECUTE_READ      0x20
-#define PAGE_EXECUTE_READWRITE 0x40
-#define PAGE_EXECUTE_WRITECOPY 0x80
-#define PAGE_GUARD            0x100
-#define PAGE_NOCACHE          0x200
-#define PAGE_WRITECOMBINE     0x400
-
-#define MEM_COMMIT           0x1000
-#define MEM_RESERVE          0x2000
-#define MEM_DECOMMIT         0x4000
-#define MEM_RELEASE          0x8000
-#define MEM_FREE            0x10000
-#define MEM_PRIVATE         0x20000
-#define MEM_MAPPED          0x40000
-#define MEM_RESET           0x80000
-#define MEM_TOP_DOWN       0x100000
-#define MEM_LARGE_PAGES  0x20000000
-#define MEM_4MB_PAGES    0x80000000
-
-
-
-
-GB_DLL_IMPORT void * WINAPI VirtualAlloc (void *addr, size_t size, DWORD allocation_type, DWORD protect);
-GB_DLL_IMPORT size_t  WINAPI VirtualQuery (void const *address, MEMORY_BASIC_INFORMATION *buffer, size_t length);
-GB_DLL_IMPORT BOOL   WINAPI VirtualFree  (void *address, size_t size, DWORD free_type);
-GB_DLL_IMPORT void   WINAPI GetSystemInfo(SYSTEM_INFO *system_info);
-
-
-#define GENERIC_READ             0x80000000
-#define GENERIC_WRITE            0x40000000
-#define GENERIC_EXECUTE          0x20000000
-#define GENERIC_ALL              0x10000000
-#define FILE_SHARE_READ          0x00000001
-#define FILE_SHARE_WRITE         0x00000002
-#define FILE_SHARE_DELETE        0x00000004
-#define CREATE_NEW               1
-#define CREATE_ALWAYS            2
-#define OPEN_EXISTING            3
-#define OPEN_ALWAYS              4
-#define TRUNCATE_EXISTING        5
-#define FILE_ATTRIBUTE_READONLY  0x00000001
-#define FILE_ATTRIBUTE_NORMAL    0x00000080
-#define FILE_ATTRIBUTE_TEMPORARY 0x00000100
-#define FILE_ATTRIBUTE_DIRECTORY 0x00000010
-#define ERROR_FILE_NOT_FOUND     2l
-#define ERROR_ACCESS_DENIED      5L
-#define ERROR_NO_MORE_FILES      18l
-#define ERROR_FILE_EXISTS        80l
-#define ERROR_ALREADY_EXISTS     183l
-#define STD_INPUT_HANDLE         ((DWORD)-10)
-#define STD_OUTPUT_HANDLE        ((DWORD)-11)
-#define STD_ERROR_HANDLE         ((DWORD)-12)
-
-GB_DLL_IMPORT int           MultiByteToWideChar(UINT code_page, DWORD flags, char const *   multi_byte_str, int multi_byte_len, wchar_t const *wide_char_str,  int wide_char_len);
-GB_DLL_IMPORT int           WideCharToMultiByte(UINT code_page, DWORD flags, wchar_t const *wide_char_str,  int wide_char_len, char const *    multi_byte_str, int multi_byte_len);
-GB_DLL_IMPORT BOOL   WINAPI SetFilePointerEx(HANDLE file, LARGE_INTEGER distance_to_move,
-                                             LARGE_INTEGER *new_file_pointer, DWORD move_method);
-GB_DLL_IMPORT BOOL   WINAPI ReadFile        (HANDLE file, void *buffer, DWORD bytes_to_read, DWORD *bytes_read, OVERLAPPED *overlapped);
-GB_DLL_IMPORT BOOL   WINAPI WriteFile       (HANDLE file, void const *buffer, DWORD bytes_to_write, DWORD *bytes_written, OVERLAPPED *overlapped);
-GB_DLL_IMPORT HANDLE WINAPI CreateFileW     (wchar_t const *path, DWORD desired_access, DWORD share_mode,
-                                             SECURITY_ATTRIBUTES *, DWORD creation_disposition,
-                                             DWORD flags_and_attributes, HANDLE template_file);
-GB_DLL_IMPORT HANDLE WINAPI CreateFileA     (char const *path, DWORD desired_access, DWORD share_mode,
-                                             SECURITY_ATTRIBUTES *, DWORD creation_disposition,
-                                             DWORD flags_and_attributes, HANDLE template_file);
-GB_DLL_IMPORT HANDLE WINAPI GetStdHandle    (DWORD std_handle);
-GB_DLL_IMPORT BOOL   WINAPI GetFileSizeEx   (HANDLE file, LARGE_INTEGER *size);
-GB_DLL_IMPORT BOOL   WINAPI SetEndOfFile    (HANDLE file);
-GB_DLL_IMPORT HANDLE WINAPI FindFirstFileW  (wchar_t const *path, WIN32_FIND_DATAW *data);
-GB_DLL_IMPORT HANDLE WINAPI FindFirstFileA  (char const *path, WIN32_FIND_DATAA *data);
-GB_DLL_IMPORT BOOL   WINAPI FindNextFileA   (HANDLE find_find, WIN32_FIND_DATAA *data);
-GB_DLL_IMPORT BOOL   WINAPI FindClose       (HANDLE find_file);
-GB_DLL_IMPORT BOOL   WINAPI GetFileAttributesExW(wchar_t const *path, GET_FILEEX_INFO_LEVELS info_level_id, WIN32_FILE_ATTRIBUTE_DATA *data);
-GB_DLL_IMPORT BOOL   WINAPI CopyFileW(wchar_t const *old_f, wchar_t const *new_f, BOOL fail_if_exists);
-GB_DLL_IMPORT BOOL   WINAPI MoveFileW(wchar_t const *old_f, wchar_t const *new_f);
-GB_DLL_IMPORT BOOL   WINAPI DeleteFileA     (char const *path);
-GB_DLL_IMPORT BOOL   WINAPI CreateDirectoryA(char const *path);
-GB_DLL_IMPORT BOOL   WINAPI RemoveDirectoryA(char const *path);
-GB_DLL_IMPORT BOOL   WINAPI MoveFileA       (char const *old_path, char const *new_path);
-
-GB_DLL_IMPORT DWORD  WINAPI GetFullPathNameA(char const *lpFileName, DWORD nBufferLength, char *lpBuffer, char **lpFilePart);
-
-GB_DLL_IMPORT HMODULE WINAPI LoadLibraryA  (char const *filename);
-GB_DLL_IMPORT BOOL    WINAPI FreeLibrary   (HMODULE module);
-GB_DLL_IMPORT FARPROC WINAPI GetProcAddress(HMODULE module, char const *name);
-
-GB_DLL_IMPORT BOOL WINAPI QueryPerformanceFrequency(LARGE_INTEGER *frequency);
-GB_DLL_IMPORT BOOL WINAPI QueryPerformanceCounter  (LARGE_INTEGER *counter);
-GB_DLL_IMPORT void WINAPI GetSystemTimeAsFileTime  (FILETIME *system_time_as_file_time);
-GB_DLL_IMPORT void WINAPI Sleep(DWORD milliseconds);
-GB_DLL_IMPORT void WINAPI ExitProcess(UINT exit_code);
-
-GB_DLL_IMPORT BOOL  WINAPI SetEnvironmentVariableA(char const *name, char const *value);
-GB_DLL_IMPORT DWORD WINAPI GetEnvironmentVariableA(char const * lpName, char * lpBuffer, DWORD nSize);
-
-GB_DLL_IMPORT short WINAPI htons(short hostshort);
-GB_DLL_IMPORT int   WINAPI htonl(int   hostint);
-GB_DLL_IMPORT short WINAPI ntohs(short netshort);
-GB_DLL_IMPORT int   WINAPI ntohl(int   netint);
diff --git a/include/stb_ds.h b/include/stb_ds.h
deleted file mode 100644 (file)
index e84c82d..0000000
+++ /dev/null
@@ -1,1895 +0,0 @@
-/* stb_ds.h - v0.67 - public domain data structures - Sean Barrett 2019
-
-   This is a single-header-file library that provides easy-to-use
-   dynamic arrays and hash tables for C (also works in C++).
-
-   For a gentle introduction:
-      http://nothings.org/stb_ds
-
-   To use this library, do this in *one* C or C++ file:
-      #define STB_DS_IMPLEMENTATION
-      #include "stb_ds.h"
-
-TABLE OF CONTENTS
-
-  Table of Contents
-  Compile-time options
-  License
-  Documentation
-  Notes
-  Notes - Dynamic arrays
-  Notes - Hash maps
-  Credits
-
-COMPILE-TIME OPTIONS
-
-  #define STBDS_NO_SHORT_NAMES
-
-     This flag needs to be set globally.
-
-     By default stb_ds exposes shorter function names that are not qualified
-     with the "stbds_" prefix. If these names conflict with the names in your
-     code, define this flag.
-
-  #define STBDS_SIPHASH_2_4
-
-     This flag only needs to be set in the file containing #define STB_DS_IMPLEMENTATION.
-
-     By default stb_ds.h hashes using a weaker variant of SipHash and a custom hash for
-     4- and 8-byte keys. On 64-bit platforms, you can define the above flag to force
-     stb_ds.h to use specification-compliant SipHash-2-4 for all keys. Doing so makes
-     hash table insertion about 20% slower on 4- and 8-byte keys, 5% slower on
-     64-byte keys, and 10% slower on 256-byte keys on my test computer.
-
-  #define STBDS_REALLOC(context,ptr,size) better_realloc
-  #define STBDS_FREE(context,ptr)         better_free
-
-     These defines only need to be set in the file containing #define STB_DS_IMPLEMENTATION.
-
-     By default stb_ds uses stdlib realloc() and free() for memory management. You can
-     substitute your own functions instead by defining these symbols. You must either
-     define both, or neither. Note that at the moment, 'context' will always be NULL.
-     @TODO add an array/hash initialization function that takes a memory context pointer.
-
-  #define STBDS_UNIT_TESTS
-
-     Defines a function stbds_unit_tests() that checks the functioning of the data structures.
-
-  Note that on older versions of gcc (e.g. 5.x.x) you may need to build with '-std=c++0x'
-     (or equivalentally '-std=c++11') when using anonymous structures as seen on the web
-     page or in STBDS_UNIT_TESTS.
-
-LICENSE
-
-  Placed in the public domain and also MIT licensed.
-  See end of file for detailed license information.
-
-DOCUMENTATION
-
-  Dynamic Arrays
-
-    Non-function interface:
-
-      Declare an empty dynamic array of type T
-        T* foo = NULL;
-
-      Access the i'th item of a dynamic array 'foo' of type T, T* foo:
-        foo[i]
-
-    Functions (actually macros)
-
-      arrfree:
-        void arrfree(T*);
-          Frees the array.
-
-      arrlen:
-        ptrdiff_t arrlen(T*);
-          Returns the number of elements in the array.
-
-      arrlenu:
-        size_t arrlenu(T*);
-          Returns the number of elements in the array as an unsigned type.
-
-      arrpop:
-        T arrpop(T* a)
-          Removes the final element of the array and returns it.
-
-      arrput:
-        T arrput(T* a, T b);
-          Appends the item b to the end of array a. Returns b.
-
-      arrins:
-        T arrins(T* a, int p, T b);
-          Inserts the item b into the middle of array a, into a[p],
-          moving the rest of the array over. Returns b.
-
-      arrinsn:
-        void arrinsn(T* a, int p, int n);
-          Inserts n uninitialized items into array a starting at a[p],
-          moving the rest of the array over.
-
-      arraddnptr:
-        T* arraddnptr(T* a, int n)
-          Appends n uninitialized items onto array at the end.
-          Returns a pointer to the first uninitialized item added.
-
-      arraddnindex:
-        size_t arraddnindex(T* a, int n)
-          Appends n uninitialized items onto array at the end.
-          Returns the index of the first uninitialized item added.
-
-      arrdel:
-        void arrdel(T* a, int p);
-          Deletes the element at a[p], moving the rest of the array over.
-
-      arrdeln:
-        void arrdeln(T* a, int p, int n);
-          Deletes n elements starting at a[p], moving the rest of the array over.
-
-      arrdelswap:
-        void arrdelswap(T* a, int p);
-          Deletes the element at a[p], replacing it with the element from
-          the end of the array. O(1) performance.
-
-      arrsetlen:
-        void arrsetlen(T* a, int n);
-          Changes the length of the array to n. Allocates uninitialized
-          slots at the end if necessary.
-
-      arrsetcap:
-        size_t arrsetcap(T* a, int n);
-          Sets the length of allocated storage to at least n. It will not
-          change the length of the array.
-
-      arrcap:
-        size_t arrcap(T* a);
-          Returns the number of total elements the array can contain without
-          needing to be reallocated.
-
-  Hash maps & String hash maps
-
-    Given T is a structure type: struct { TK key; TV value; }. Note that some
-    functions do not require TV value and can have other fields. For string
-    hash maps, TK must be 'char *'.
-
-    Special interface:
-
-      stbds_rand_seed:
-        void stbds_rand_seed(size_t seed);
-          For security against adversarially chosen data, you should seed the
-          library with a strong random number. Or at least seed it with time().
-
-      stbds_hash_string:
-        size_t stbds_hash_string(char *str, size_t seed);
-          Returns a hash value for a string.
-
-      stbds_hash_bytes:
-        size_t stbds_hash_bytes(void *p, size_t len, size_t seed);
-          These functions hash an arbitrary number of bytes. The function
-          uses a custom hash for 4- and 8-byte data, and a weakened version
-          of SipHash for everything else. On 64-bit platforms you can get
-          specification-compliant SipHash-2-4 on all data by defining
-          STBDS_SIPHASH_2_4, at a significant cost in speed.
-
-    Non-function interface:
-
-      Declare an empty hash map of type T
-        T* foo = NULL;
-
-      Access the i'th entry in a hash table T* foo:
-        foo[i]
-
-    Function interface (actually macros):
-
-      hmfree
-      shfree
-        void hmfree(T*);
-        void shfree(T*);
-          Frees the hashmap and sets the pointer to NULL.
-
-      hmlen
-      shlen
-        ptrdiff_t hmlen(T*)
-        ptrdiff_t shlen(T*)
-          Returns the number of elements in the hashmap.
-
-      hmlenu
-      shlenu
-        size_t hmlenu(T*)
-        size_t shlenu(T*)
-          Returns the number of elements in the hashmap.
-
-      hmgeti
-      shgeti
-      hmgeti_ts
-        ptrdiff_t hmgeti(T*, TK key)
-        ptrdiff_t shgeti(T*, char* key)
-        ptrdiff_t hmgeti_ts(T*, TK key, ptrdiff_t tempvar)
-          Returns the index in the hashmap which has the key 'key', or -1
-          if the key is not present.
-
-      hmget
-      hmget_ts
-      shget
-        TV hmget(T*, TK key)
-        TV shget(T*, char* key)
-        TV hmget_ts(T*, TK key, ptrdiff_t tempvar)
-          Returns the value corresponding to 'key' in the hashmap.
-          The structure must have a 'value' field
-
-      hmgets
-      shgets
-        T hmgets(T*, TK key)
-        T shgets(T*, char* key)
-          Returns the structure corresponding to 'key' in the hashmap.
-
-      hmgetp
-      shgetp
-      hmgetp_ts
-      hmgetp_null
-      shgetp_null
-        T* hmgetp(T*, TK key)
-        T* shgetp(T*, char* key)
-        T* hmgetp_ts(T*, TK key, ptrdiff_t tempvar)
-        T* hmgetp_null(T*, TK key)
-        T* shgetp_null(T*, char *key)
-          Returns a pointer to the structure corresponding to 'key' in
-          the hashmap. Functions ending in "_null" return NULL if the key
-          is not present in the hashmap; the others return a pointer to a
-          structure holding the default value (but not the searched-for key).
-
-      hmdefault
-      shdefault
-        TV hmdefault(T*, TV value)
-        TV shdefault(T*, TV value)
-          Sets the default value for the hashmap, the value which will be
-          returned by hmget/shget if the key is not present.
-
-      hmdefaults
-      shdefaults
-        TV hmdefaults(T*, T item)
-        TV shdefaults(T*, T item)
-          Sets the default struct for the hashmap, the contents which will be
-          returned by hmgets/shgets if the key is not present.
-
-      hmput
-      shput
-        TV hmput(T*, TK key, TV value)
-        TV shput(T*, char* key, TV value)
-          Inserts a <key,value> pair into the hashmap. If the key is already
-          present in the hashmap, updates its value.
-
-      hmputs
-      shputs
-        T hmputs(T*, T item)
-        T shputs(T*, T item)
-          Inserts a struct with T.key into the hashmap. If the struct is already
-          present in the hashmap, updates it.
-
-      hmdel
-      shdel
-        int hmdel(T*, TK key)
-        int shdel(T*, char* key)
-          If 'key' is in the hashmap, deletes its entry and returns 1.
-          Otherwise returns 0.
-
-    Function interface (actually macros) for strings only:
-
-      sh_new_strdup
-        void sh_new_strdup(T*);
-          Overwrites the existing pointer with a newly allocated
-          string hashmap which will automatically allocate and free
-          each string key using realloc/free
-
-      sh_new_arena
-        void sh_new_arena(T*);
-          Overwrites the existing pointer with a newly allocated
-          string hashmap which will automatically allocate each string
-          key to a string arena. Every string key ever used by this
-          hash table remains in the arena until the arena is freed.
-          Additionally, any key which is deleted and reinserted will
-          be allocated multiple times in the string arena.
-
-NOTES
-
-  * These data structures are realloc'd when they grow, and the macro
-    "functions" write to the provided pointer. This means: (a) the pointer
-    must be an lvalue, and (b) the pointer to the data structure is not
-    stable, and you must maintain it the same as you would a realloc'd
-    pointer. For example, if you pass a pointer to a dynamic array to a
-    function which updates it, the function must return back the new
-    pointer to the caller. This is the price of trying to do this in C.
-
-  * The following are the only functions that are thread-safe on a single data
-    structure, i.e. can be run in multiple threads simultaneously on the same
-    data structure
-        hmlen        shlen
-        hmlenu       shlenu
-        hmget_ts     shget_ts
-        hmgeti_ts    shgeti_ts
-        hmgets_ts    shgets_ts
-
-  * You iterate over the contents of a dynamic array and a hashmap in exactly
-    the same way, using arrlen/hmlen/shlen:
-
-      for (i=0; i < arrlen(foo); ++i)
-         ... foo[i] ...
-
-  * All operations except arrins/arrdel are O(1) amortized, but individual
-    operations can be slow, so these data structures may not be suitable
-    for real time use. Dynamic arrays double in capacity as needed, so
-    elements are copied an average of once. Hash tables double/halve
-    their size as needed, with appropriate hysteresis to maintain O(1)
-    performance.
-
-NOTES - DYNAMIC ARRAY
-
-  * If you know how long a dynamic array is going to be in advance, you can avoid
-    extra memory allocations by using arrsetlen to allocate it to that length in
-    advance and use foo[n] while filling it out, or arrsetcap to allocate the memory
-    for that length and use arrput/arrpush as normal.
-
-  * Unlike some other versions of the dynamic array, this version should
-    be safe to use with strict-aliasing optimizations.
-
-NOTES - HASH MAP
-
-  * For compilers other than GCC and clang (e.g. Visual Studio), for hmput/hmget/hmdel
-    and variants, the key must be an lvalue (so the macro can take the address of it).
-    Extensions are used that eliminate this requirement if you're using C99 and later
-    in GCC or clang, or if you're using C++ in GCC. But note that this can make your
-    code less portable.
-
-  * To test for presence of a key in a hashmap, just do 'hmgeti(foo,key) >= 0'.
-
-  * The iteration order of your data in the hashmap is determined solely by the
-    order of insertions and deletions. In particular, if you never delete, new
-    keys are always added at the end of the array. This will be consistent
-    across all platforms and versions of the library. However, you should not
-    attempt to serialize the internal hash table, as the hash is not consistent
-    between different platforms, and may change with future versions of the library.
-
-  * Use sh_new_arena() for string hashmaps that you never delete from. Initialize
-    with NULL if you're managing the memory for your strings, or your strings are
-    never freed (at least until the hashmap is freed). Otherwise, use sh_new_strdup().
-    @TODO: make an arena variant that garbage collects the strings with a trivial
-    copy collector into a new arena whenever the table shrinks / rebuilds. Since
-    current arena recommendation is to only use arena if it never deletes, then
-    this can just replace current arena implementation.
-
-  * If adversarial input is a serious concern and you're on a 64-bit platform,
-    enable STBDS_SIPHASH_2_4 (see the 'Compile-time options' section), and pass
-    a strong random number to stbds_rand_seed.
-
-  * The default value for the hash table is stored in foo[-1], so if you
-    use code like 'hmget(T,k)->value = 5' you can accidentally overwrite
-    the value stored by hmdefault if 'k' is not present.
-
-CREDITS
-
-  Sean Barrett -- library, idea for dynamic array API/implementation
-  Per Vognsen  -- idea for hash table API/implementation
-  Rafael Sachetto -- arrpop()
-  github:HeroicKatora -- arraddn() reworking
-
-  Bugfixes:
-    Andy Durdin
-    Shane Liesegang
-    Vinh Truong
-    Andreas Molzer
-    github:hashitaku
-    github:srdjanstipic
-    Macoy Madson
-    Andreas Vennstrom
-    Tobias Mansfield-Williams
-*/
-
-#ifdef STBDS_UNIT_TESTS
-#define _CRT_SECURE_NO_WARNINGS
-#endif
-
-#ifndef INCLUDE_STB_DS_H
-#define INCLUDE_STB_DS_H
-
-#include <stddef.h>
-#include <string.h>
-
-#ifndef STBDS_NO_SHORT_NAMES
-#define arrlen      stbds_arrlen
-#define arrlenu     stbds_arrlenu
-#define arrput      stbds_arrput
-#define arrpush     stbds_arrput
-#define arrpop      stbds_arrpop
-#define arrfree     stbds_arrfree
-#define arraddn     stbds_arraddn // deprecated, use one of the following instead:
-#define arraddnptr  stbds_arraddnptr
-#define arraddnindex stbds_arraddnindex
-#define arrsetlen   stbds_arrsetlen
-#define arrlast     stbds_arrlast
-#define arrins      stbds_arrins
-#define arrinsn     stbds_arrinsn
-#define arrdel      stbds_arrdel
-#define arrdeln     stbds_arrdeln
-#define arrdelswap  stbds_arrdelswap
-#define arrcap      stbds_arrcap
-#define arrsetcap   stbds_arrsetcap
-
-#define hmput       stbds_hmput
-#define hmputs      stbds_hmputs
-#define hmget       stbds_hmget
-#define hmget_ts    stbds_hmget_ts
-#define hmgets      stbds_hmgets
-#define hmgetp      stbds_hmgetp
-#define hmgetp_ts   stbds_hmgetp_ts
-#define hmgetp_null stbds_hmgetp_null
-#define hmgeti      stbds_hmgeti
-#define hmgeti_ts   stbds_hmgeti_ts
-#define hmdel       stbds_hmdel
-#define hmlen       stbds_hmlen
-#define hmlenu      stbds_hmlenu
-#define hmfree      stbds_hmfree
-#define hmdefault   stbds_hmdefault
-#define hmdefaults  stbds_hmdefaults
-
-#define shput       stbds_shput
-#define shputi      stbds_shputi
-#define shputs      stbds_shputs
-#define shget       stbds_shget
-#define shgeti      stbds_shgeti
-#define shgets      stbds_shgets
-#define shgetp      stbds_shgetp
-#define shgetp_null stbds_shgetp_null
-#define shdel       stbds_shdel
-#define shlen       stbds_shlen
-#define shlenu      stbds_shlenu
-#define shfree      stbds_shfree
-#define shdefault   stbds_shdefault
-#define shdefaults  stbds_shdefaults
-#define sh_new_arena  stbds_sh_new_arena
-#define sh_new_strdup stbds_sh_new_strdup
-
-#define stralloc    stbds_stralloc
-#define strreset    stbds_strreset
-#endif
-
-#if defined(STBDS_REALLOC) && !defined(STBDS_FREE) || !defined(STBDS_REALLOC) && defined(STBDS_FREE)
-#error "You must define both STBDS_REALLOC and STBDS_FREE, or neither."
-#endif
-#if !defined(STBDS_REALLOC) && !defined(STBDS_FREE)
-#include <stdlib.h>
-#define STBDS_REALLOC(c,p,s) realloc(p,s)
-#define STBDS_FREE(c,p)      free(p)
-#endif
-
-#ifdef _MSC_VER
-#define STBDS_NOTUSED(v)  (void)(v)
-#else
-#define STBDS_NOTUSED(v)  (void)sizeof(v)
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// for security against attackers, seed the library with a random number, at least time() but stronger is better
-extern void stbds_rand_seed(size_t seed);
-
-// these are the hash functions used internally if you want to test them or use them for other purposes
-extern size_t stbds_hash_bytes(void *p, size_t len, size_t seed);
-extern size_t stbds_hash_string(char *str, size_t seed);
-
-// this is a simple string arena allocator, initialize with e.g. 'stbds_string_arena my_arena={0}'.
-typedef struct stbds_string_arena stbds_string_arena;
-extern char * stbds_stralloc(stbds_string_arena *a, char *str);
-extern void   stbds_strreset(stbds_string_arena *a);
-
-// have to #define STBDS_UNIT_TESTS to call this
-extern void stbds_unit_tests(void);
-
-///////////////
-//
-// Everything below here is implementation details
-//
-
-extern void * stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap);
-extern void   stbds_arrfreef(void *a);
-extern void   stbds_hmfree_func(void *p, size_t elemsize);
-extern void * stbds_hmget_key(void *a, size_t elemsize, void *key, size_t keysize, int mode);
-extern void * stbds_hmget_key_ts(void *a, size_t elemsize, void *key, size_t keysize, ptrdiff_t *temp, int mode);
-extern void * stbds_hmput_default(void *a, size_t elemsize);
-extern void * stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, int mode);
-extern void * stbds_hmdel_key(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode);
-extern void * stbds_shmode_func(size_t elemsize, int mode);
-
-#ifdef __cplusplus
-}
-#endif
-
-#if defined(__GNUC__) || defined(__clang__)
-#define STBDS_HAS_TYPEOF
-#ifdef __cplusplus
-//#define STBDS_HAS_LITERAL_ARRAY  // this is currently broken for clang
-#endif
-#endif
-
-#if !defined(__cplusplus)
-#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
-#define STBDS_HAS_LITERAL_ARRAY
-#endif
-#endif
-
-// this macro takes the address of the argument, but on gcc/clang can accept rvalues
-#if defined(STBDS_HAS_LITERAL_ARRAY) && defined(STBDS_HAS_TYPEOF)
-  #if __clang__
-  #define STBDS_ADDRESSOF(typevar, value)     ((__typeof__(typevar)[1]){value}) // literal array decays to pointer to value
-  #else
-  #define STBDS_ADDRESSOF(typevar, value)     ((typeof(typevar)[1]){value}) // literal array decays to pointer to value
-  #endif
-#else
-#define STBDS_ADDRESSOF(typevar, value)     &(value)
-#endif
-
-#define STBDS_OFFSETOF(var,field)           ((char *) &(var)->field - (char *) (var))
-
-#define stbds_header(t)  ((stbds_array_header *) (t) - 1)
-#define stbds_temp(t)    stbds_header(t)->temp
-#define stbds_temp_key(t) (*(char **) stbds_header(t)->hash_table)
-
-#define stbds_arrsetcap(a,n)   (stbds_arrgrow(a,0,n))
-#define stbds_arrsetlen(a,n)   ((stbds_arrcap(a) < (size_t) (n) ? stbds_arrsetcap((a),(size_t)(n)),0 : 0), (a) ? stbds_header(a)->length = (size_t) (n) : 0)
-#define stbds_arrcap(a)        ((a) ? stbds_header(a)->capacity : 0)
-#define stbds_arrlen(a)        ((a) ? (ptrdiff_t) stbds_header(a)->length : 0)
-#define stbds_arrlenu(a)       ((a) ?             stbds_header(a)->length : 0)
-#define stbds_arrput(a,v)      (stbds_arrmaybegrow(a,1), (a)[stbds_header(a)->length++] = (v))
-#define stbds_arrpush          stbds_arrput  // synonym
-#define stbds_arrpop(a)        (stbds_header(a)->length--, (a)[stbds_header(a)->length])
-#define stbds_arraddn(a,n)     ((void)(stbds_arraddnindex(a, n)))    // deprecated, use one of the following instead:
-#define stbds_arraddnptr(a,n)  (stbds_arrmaybegrow(a,n), (n) ? (stbds_header(a)->length += (n), &(a)[stbds_header(a)->length-(n)]) : (a))
-#define stbds_arraddnindex(a,n)(stbds_arrmaybegrow(a,n), (n) ? (stbds_header(a)->length += (n), stbds_header(a)->length-(n)) : stbds_arrlen(a))
-#define stbds_arraddnoff       stbds_arraddnindex
-#define stbds_arrlast(a)       ((a)[stbds_header(a)->length-1])
-#define stbds_arrfree(a)       ((void) ((a) ? STBDS_FREE(NULL,stbds_header(a)) : (void)0), (a)=NULL)
-#define stbds_arrdel(a,i)      stbds_arrdeln(a,i,1)
-#define stbds_arrdeln(a,i,n)   (memmove(&(a)[i], &(a)[(i)+(n)], sizeof *(a) * (stbds_header(a)->length-(n)-(i))), stbds_header(a)->length -= (n))
-#define stbds_arrdelswap(a,i)  ((a)[i] = stbds_arrlast(a), stbds_header(a)->length -= 1)
-#define stbds_arrinsn(a,i,n)   (stbds_arraddn((a),(n)), memmove(&(a)[(i)+(n)], &(a)[i], sizeof *(a) * (stbds_header(a)->length-(n)-(i))))
-#define stbds_arrins(a,i,v)    (stbds_arrinsn((a),(i),1), (a)[i]=(v))
-
-#define stbds_arrmaybegrow(a,n)  ((!(a) || stbds_header(a)->length + (n) > stbds_header(a)->capacity) \
-                                  ? (stbds_arrgrow(a,n,0),0) : 0)
-
-#define stbds_arrgrow(a,b,c)   ((a) = stbds_arrgrowf_wrapper((a), sizeof *(a), (b), (c)))
-
-#define stbds_hmput(t, k, v) \
-    ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, 0),   \
-     (t)[stbds_temp((t)-1)].key = (k),    \
-     (t)[stbds_temp((t)-1)].value = (v))
-
-#define stbds_hmputs(t, s) \
-    ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), &(s).key, sizeof (s).key, STBDS_HM_BINARY), \
-     (t)[stbds_temp((t)-1)] = (s))
-
-#define stbds_hmgeti(t,k) \
-    ((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, STBDS_HM_BINARY), \
-      stbds_temp((t)-1))
-
-#define stbds_hmgeti_ts(t,k,temp) \
-    ((t) = stbds_hmget_key_ts_wrapper((t), sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, &(temp), STBDS_HM_BINARY), \
-      (temp))
-
-#define stbds_hmgetp(t, k) \
-    ((void) stbds_hmgeti(t,k), &(t)[stbds_temp((t)-1)])
-
-#define stbds_hmgetp_ts(t, k, temp) \
-    ((void) stbds_hmgeti_ts(t,k,temp), &(t)[temp])
-
-#define stbds_hmdel(t,k) \
-    (((t) = stbds_hmdel_key_wrapper((t),sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, STBDS_OFFSETOF((t),key), STBDS_HM_BINARY)),(t)?stbds_temp((t)-1):0)
-
-#define stbds_hmdefault(t, v) \
-    ((t) = stbds_hmput_default_wrapper((t), sizeof *(t)), (t)[-1].value = (v))
-
-#define stbds_hmdefaults(t, s) \
-    ((t) = stbds_hmput_default_wrapper((t), sizeof *(t)), (t)[-1] = (s))
-
-#define stbds_hmfree(p)        \
-    ((void) ((p) != NULL ? stbds_hmfree_func((p)-1,sizeof*(p)),0 : 0),(p)=NULL)
-
-#define stbds_hmgets(t, k)    (*stbds_hmgetp(t,k))
-#define stbds_hmget(t, k)     (stbds_hmgetp(t,k)->value)
-#define stbds_hmget_ts(t, k, temp)  (stbds_hmgetp_ts(t,k,temp)->value)
-#define stbds_hmlen(t)        ((t) ? (ptrdiff_t) stbds_header((t)-1)->length-1 : 0)
-#define stbds_hmlenu(t)       ((t) ?             stbds_header((t)-1)->length-1 : 0)
-#define stbds_hmgetp_null(t,k)  (stbds_hmgeti(t,k) == -1 ? NULL : &(t)[stbds_temp((t)-1)])
-
-#define stbds_shput(t, k, v) \
-    ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING),   \
-     (t)[stbds_temp((t)-1)].value = (v))
-
-#define stbds_shputi(t, k, v) \
-    ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING),   \
-     (t)[stbds_temp((t)-1)].value = (v), stbds_temp((t)-1))
-
-#define stbds_shputs(t, s) \
-    ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (s).key, sizeof (s).key, STBDS_HM_STRING), \
-     (t)[stbds_temp((t)-1)] = (s), \
-     (t)[stbds_temp((t)-1)].key = stbds_temp_key((t)-1)) // above line overwrites whole structure, so must rewrite key here if it was allocated internally
-
-#define stbds_pshput(t, p) \
-    ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (p)->key, sizeof (p)->key, STBDS_HM_PTR_TO_STRING), \
-     (t)[stbds_temp((t)-1)] = (p))
-
-#define stbds_shgeti(t,k) \
-     ((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING), \
-      stbds_temp((t)-1))
-
-#define stbds_pshgeti(t,k) \
-     ((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (*(t))->key, STBDS_HM_PTR_TO_STRING), \
-      stbds_temp((t)-1))
-
-#define stbds_shgetp(t, k) \
-    ((void) stbds_shgeti(t,k), &(t)[stbds_temp((t)-1)])
-
-#define stbds_pshget(t, k) \
-    ((void) stbds_pshgeti(t,k), (t)[stbds_temp((t)-1)])
-
-#define stbds_shdel(t,k) \
-    (((t) = stbds_hmdel_key_wrapper((t),sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_OFFSETOF((t),key), STBDS_HM_STRING)),(t)?stbds_temp((t)-1):0)
-#define stbds_pshdel(t,k) \
-    (((t) = stbds_hmdel_key_wrapper((t),sizeof *(t), (void*) (k), sizeof (*(t))->key, STBDS_OFFSETOF(*(t),key), STBDS_HM_PTR_TO_STRING)),(t)?stbds_temp((t)-1):0)
-
-#define stbds_sh_new_arena(t)  \
-    ((t) = stbds_shmode_func_wrapper(t, sizeof *(t), STBDS_SH_ARENA))
-#define stbds_sh_new_strdup(t) \
-    ((t) = stbds_shmode_func_wrapper(t, sizeof *(t), STBDS_SH_STRDUP))
-
-#define stbds_shdefault(t, v)  stbds_hmdefault(t,v)
-#define stbds_shdefaults(t, s) stbds_hmdefaults(t,s)
-
-#define stbds_shfree       stbds_hmfree
-#define stbds_shlenu       stbds_hmlenu
-
-#define stbds_shgets(t, k) (*stbds_shgetp(t,k))
-#define stbds_shget(t, k)  (stbds_shgetp(t,k)->value)
-#define stbds_shgetp_null(t,k)  (stbds_shgeti(t,k) == -1 ? NULL : &(t)[stbds_temp((t)-1)])
-#define stbds_shlen        stbds_hmlen
-
-typedef struct
-{
-  size_t      length;
-  size_t      capacity;
-  void      * hash_table;
-  ptrdiff_t   temp;
-} stbds_array_header;
-
-typedef struct stbds_string_block
-{
-  struct stbds_string_block *next;
-  char storage[8];
-} stbds_string_block;
-
-struct stbds_string_arena
-{
-  stbds_string_block *storage;
-  size_t remaining;
-  unsigned char block;
-  unsigned char mode;  // this isn't used by the string arena itself
-};
-
-#define STBDS_HM_BINARY         0
-#define STBDS_HM_STRING         1
-
-enum
-{
-   STBDS_SH_NONE,
-   STBDS_SH_DEFAULT,
-   STBDS_SH_STRDUP,
-   STBDS_SH_ARENA
-};
-
-#ifdef __cplusplus
-// in C we use implicit assignment from these void*-returning functions to T*.
-// in C++ these templates make the same code work
-template<class T> static T * stbds_arrgrowf_wrapper(T *a, size_t elemsize, size_t addlen, size_t min_cap) {
-  return (T*)stbds_arrgrowf((void *)a, elemsize, addlen, min_cap);
-}
-template<class T> static T * stbds_hmget_key_wrapper(T *a, size_t elemsize, void *key, size_t keysize, int mode) {
-  return (T*)stbds_hmget_key((void*)a, elemsize, key, keysize, mode);
-}
-template<class T> static T * stbds_hmget_key_ts_wrapper(T *a, size_t elemsize, void *key, size_t keysize, ptrdiff_t *temp, int mode) {
-  return (T*)stbds_hmget_key_ts((void*)a, elemsize, key, keysize, temp, mode);
-}
-template<class T> static T * stbds_hmput_default_wrapper(T *a, size_t elemsize) {
-  return (T*)stbds_hmput_default((void *)a, elemsize);
-}
-template<class T> static T * stbds_hmput_key_wrapper(T *a, size_t elemsize, void *key, size_t keysize, int mode) {
-  return (T*)stbds_hmput_key((void*)a, elemsize, key, keysize, mode);
-}
-template<class T> static T * stbds_hmdel_key_wrapper(T *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode){
-  return (T*)stbds_hmdel_key((void*)a, elemsize, key, keysize, keyoffset, mode);
-}
-template<class T> static T * stbds_shmode_func_wrapper(T *, size_t elemsize, int mode) {
-  return (T*)stbds_shmode_func(elemsize, mode);
-}
-#else
-#define stbds_arrgrowf_wrapper            stbds_arrgrowf
-#define stbds_hmget_key_wrapper           stbds_hmget_key
-#define stbds_hmget_key_ts_wrapper        stbds_hmget_key_ts
-#define stbds_hmput_default_wrapper       stbds_hmput_default
-#define stbds_hmput_key_wrapper           stbds_hmput_key
-#define stbds_hmdel_key_wrapper           stbds_hmdel_key
-#define stbds_shmode_func_wrapper(t,e,m)  stbds_shmode_func(e,m)
-#endif
-
-#endif // INCLUDE_STB_DS_H
-
-
-//////////////////////////////////////////////////////////////////////////////
-//
-//   IMPLEMENTATION
-//
-
-#ifdef STB_DS_IMPLEMENTATION
-#include <assert.h>
-#include <string.h>
-
-#ifndef STBDS_ASSERT
-#define STBDS_ASSERT_WAS_UNDEFINED
-#define STBDS_ASSERT(x)   ((void) 0)
-#endif
-
-#ifdef STBDS_STATISTICS
-#define STBDS_STATS(x)   x
-size_t stbds_array_grow;
-size_t stbds_hash_grow;
-size_t stbds_hash_shrink;
-size_t stbds_hash_rebuild;
-size_t stbds_hash_probes;
-size_t stbds_hash_alloc;
-size_t stbds_rehash_probes;
-size_t stbds_rehash_items;
-#else
-#define STBDS_STATS(x)
-#endif
-
-//
-// stbds_arr implementation
-//
-
-//int *prev_allocs[65536];
-//int num_prev;
-
-void *stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap)
-{
-  stbds_array_header temp={0}; // force debugging
-  void *b;
-  size_t min_len = stbds_arrlen(a) + addlen;
-  (void) sizeof(temp);
-
-  // compute the minimum capacity needed
-  if (min_len > min_cap)
-    min_cap = min_len;
-
-  if (min_cap <= stbds_arrcap(a))
-    return a;
-
-  // increase needed capacity to guarantee O(1) amortized
-  if (min_cap < 2 * stbds_arrcap(a))
-    min_cap = 2 * stbds_arrcap(a);
-  else if (min_cap < 4)
-    min_cap = 4;
-
-  //if (num_prev < 65536) if (a) prev_allocs[num_prev++] = (int *) ((char *) a+1);
-  //if (num_prev == 2201)
-  //  num_prev = num_prev;
-  b = STBDS_REALLOC(NULL, (a) ? stbds_header(a) : 0, elemsize * min_cap + sizeof(stbds_array_header));
-  //if (num_prev < 65536) prev_allocs[num_prev++] = (int *) (char *) b;
-  b = (char *) b + sizeof(stbds_array_header);
-  if (a == NULL) {
-    stbds_header(b)->length = 0;
-    stbds_header(b)->hash_table = 0;
-    stbds_header(b)->temp = 0;
-  } else {
-    STBDS_STATS(++stbds_array_grow);
-  }
-  stbds_header(b)->capacity = min_cap;
-
-  return b;
-}
-
-void stbds_arrfreef(void *a)
-{
-  STBDS_FREE(NULL, stbds_header(a));
-}
-
-//
-// stbds_hm hash table implementation
-//
-
-#ifdef STBDS_INTERNAL_SMALL_BUCKET
-#define STBDS_BUCKET_LENGTH      4
-#else
-#define STBDS_BUCKET_LENGTH      8
-#endif
-
-#define STBDS_BUCKET_SHIFT      (STBDS_BUCKET_LENGTH == 8 ? 3 : 2)
-#define STBDS_BUCKET_MASK       (STBDS_BUCKET_LENGTH-1)
-#define STBDS_CACHE_LINE_SIZE   64
-
-#define STBDS_ALIGN_FWD(n,a)   (((n) + (a) - 1) & ~((a)-1))
-
-typedef struct
-{
-   size_t    hash [STBDS_BUCKET_LENGTH];
-   ptrdiff_t index[STBDS_BUCKET_LENGTH];
-} stbds_hash_bucket; // in 32-bit, this is one 64-byte cache line; in 64-bit, each array is one 64-byte cache line
-
-typedef struct
-{
-  char * temp_key; // this MUST be the first field of the hash table
-  size_t slot_count;
-  size_t used_count;
-  size_t used_count_threshold;
-  size_t used_count_shrink_threshold;
-  size_t tombstone_count;
-  size_t tombstone_count_threshold;
-  size_t seed;
-  size_t slot_count_log2;
-  stbds_string_arena string;
-  stbds_hash_bucket *storage; // not a separate allocation, just 64-byte aligned storage after this struct
-} stbds_hash_index;
-
-#define STBDS_INDEX_EMPTY    -1
-#define STBDS_INDEX_DELETED  -2
-#define STBDS_INDEX_IN_USE(x)  ((x) >= 0)
-
-#define STBDS_HASH_EMPTY      0
-#define STBDS_HASH_DELETED    1
-
-static size_t stbds_hash_seed=0x31415926;
-
-void stbds_rand_seed(size_t seed)
-{
-  stbds_hash_seed = seed;
-}
-
-#define stbds_load_32_or_64(var, temp, v32, v64_hi, v64_lo)                                          \
-  temp = v64_lo ^ v32, temp <<= 16, temp <<= 16, temp >>= 16, temp >>= 16, /* discard if 32-bit */   \
-  var = v64_hi, var <<= 16, var <<= 16,                                    /* discard if 32-bit */   \
-  var ^= temp ^ v32
-
-#define STBDS_SIZE_T_BITS           ((sizeof (size_t)) * 8)
-
-static size_t stbds_probe_position(size_t hash, size_t slot_count, size_t slot_log2)
-{
-  size_t pos;
-  STBDS_NOTUSED(slot_log2);
-  pos = hash & (slot_count-1);
-  #ifdef STBDS_INTERNAL_BUCKET_START
-  pos &= ~STBDS_BUCKET_MASK;
-  #endif
-  return pos;
-}
-
-static size_t stbds_log2(size_t slot_count)
-{
-  size_t n=0;
-  while (slot_count > 1) {
-    slot_count >>= 1;
-    ++n;
-  }
-  return n;
-}
-
-static stbds_hash_index *stbds_make_hash_index(size_t slot_count, stbds_hash_index *ot)
-{
-  stbds_hash_index *t;
-  t = (stbds_hash_index *) STBDS_REALLOC(NULL,0,(slot_count >> STBDS_BUCKET_SHIFT) * sizeof(stbds_hash_bucket) + sizeof(stbds_hash_index) + STBDS_CACHE_LINE_SIZE-1);
-  t->storage = (stbds_hash_bucket *) STBDS_ALIGN_FWD((size_t) (t+1), STBDS_CACHE_LINE_SIZE);
-  t->slot_count = slot_count;
-  t->slot_count_log2 = stbds_log2(slot_count);
-  t->tombstone_count = 0;
-  t->used_count = 0;
-
-  #if 0 // A1
-  t->used_count_threshold        = slot_count*12/16; // if 12/16th of table is occupied, grow
-  t->tombstone_count_threshold   = slot_count* 2/16; // if tombstones are 2/16th of table, rebuild
-  t->used_count_shrink_threshold = slot_count* 4/16; // if table is only 4/16th full, shrink
-  #elif 1 // A2
-  //t->used_count_threshold        = slot_count*12/16; // if 12/16th of table is occupied, grow
-  //t->tombstone_count_threshold   = slot_count* 3/16; // if tombstones are 3/16th of table, rebuild
-  //t->used_count_shrink_threshold = slot_count* 4/16; // if table is only 4/16th full, shrink
-
-  // compute without overflowing
-  t->used_count_threshold        = slot_count - (slot_count>>2);
-  t->tombstone_count_threshold   = (slot_count>>3) + (slot_count>>4);
-  t->used_count_shrink_threshold = slot_count >> 2;
-
-  #elif 0 // B1
-  t->used_count_threshold        = slot_count*13/16; // if 13/16th of table is occupied, grow
-  t->tombstone_count_threshold   = slot_count* 2/16; // if tombstones are 2/16th of table, rebuild
-  t->used_count_shrink_threshold = slot_count* 5/16; // if table is only 5/16th full, shrink
-  #else // C1
-  t->used_count_threshold        = slot_count*14/16; // if 14/16th of table is occupied, grow
-  t->tombstone_count_threshold   = slot_count* 2/16; // if tombstones are 2/16th of table, rebuild
-  t->used_count_shrink_threshold = slot_count* 6/16; // if table is only 6/16th full, shrink
-  #endif
-  // Following statistics were measured on a Core i7-6700 @ 4.00Ghz, compiled with clang 7.0.1 -O2
-    // Note that the larger tables have high variance as they were run fewer times
-  //     A1            A2          B1           C1
-  //    0.10ms :     0.10ms :     0.10ms :     0.11ms :      2,000 inserts creating 2K table
-  //    0.96ms :     0.95ms :     0.97ms :     1.04ms :     20,000 inserts creating 20K table
-  //   14.48ms :    14.46ms :    10.63ms :    11.00ms :    200,000 inserts creating 200K table
-  //  195.74ms :   196.35ms :   203.69ms :   214.92ms :  2,000,000 inserts creating 2M table
-  // 2193.88ms :  2209.22ms :  2285.54ms :  2437.17ms : 20,000,000 inserts creating 20M table
-  //   65.27ms :    53.77ms :    65.33ms :    65.47ms : 500,000 inserts & deletes in 2K table
-  //   72.78ms :    62.45ms :    71.95ms :    72.85ms : 500,000 inserts & deletes in 20K table
-  //   89.47ms :    77.72ms :    96.49ms :    96.75ms : 500,000 inserts & deletes in 200K table
-  //   97.58ms :    98.14ms :    97.18ms :    97.53ms : 500,000 inserts & deletes in 2M table
-  //  118.61ms :   119.62ms :   120.16ms :   118.86ms : 500,000 inserts & deletes in 20M table
-  //  192.11ms :   194.39ms :   196.38ms :   195.73ms : 500,000 inserts & deletes in 200M table
-
-  if (slot_count <= STBDS_BUCKET_LENGTH)
-    t->used_count_shrink_threshold = 0;
-  // to avoid infinite loop, we need to guarantee that at least one slot is empty and will terminate probes
-  STBDS_ASSERT(t->used_count_threshold + t->tombstone_count_threshold < t->slot_count);
-  STBDS_STATS(++stbds_hash_alloc);
-  if (ot) {
-    t->string = ot->string;
-    // reuse old seed so we can reuse old hashes so below "copy out old data" doesn't do any hashing
-    t->seed = ot->seed;
-  } else {
-    size_t a,b,temp;
-    memset(&t->string, 0, sizeof(t->string));
-    t->seed = stbds_hash_seed;
-    // LCG
-    // in 32-bit, a =          2147001325   b =  715136305
-    // in 64-bit, a = 2862933555777941757   b = 3037000493
-    stbds_load_32_or_64(a,temp, 2147001325, 0x27bb2ee6, 0x87b0b0fd);
-    stbds_load_32_or_64(b,temp,  715136305,          0, 0xb504f32d);
-    stbds_hash_seed = stbds_hash_seed  * a + b;
-  }
-
-  {
-    size_t i,j;
-    for (i=0; i < slot_count >> STBDS_BUCKET_SHIFT; ++i) {
-      stbds_hash_bucket *b = &t->storage[i];
-      for (j=0; j < STBDS_BUCKET_LENGTH; ++j)
-        b->hash[j] = STBDS_HASH_EMPTY;
-      for (j=0; j < STBDS_BUCKET_LENGTH; ++j)
-        b->index[j] = STBDS_INDEX_EMPTY;
-    }
-  }
-
-  // copy out the old data, if any
-  if (ot) {
-    size_t i,j;
-    t->used_count = ot->used_count;
-    for (i=0; i < ot->slot_count >> STBDS_BUCKET_SHIFT; ++i) {
-      stbds_hash_bucket *ob = &ot->storage[i];
-      for (j=0; j < STBDS_BUCKET_LENGTH; ++j) {
-        if (STBDS_INDEX_IN_USE(ob->index[j])) {
-          size_t hash = ob->hash[j];
-          size_t pos = stbds_probe_position(hash, t->slot_count, t->slot_count_log2);
-          size_t step = STBDS_BUCKET_LENGTH;
-          STBDS_STATS(++stbds_rehash_items);
-          for (;;) {
-            size_t limit,z;
-            stbds_hash_bucket *bucket;
-            bucket = &t->storage[pos >> STBDS_BUCKET_SHIFT];
-            STBDS_STATS(++stbds_rehash_probes);
-
-            for (z=pos & STBDS_BUCKET_MASK; z < STBDS_BUCKET_LENGTH; ++z) {
-              if (bucket->hash[z] == 0) {
-                bucket->hash[z] = hash;
-                bucket->index[z] = ob->index[j];
-                goto done;
-              }
-            }
-
-            limit = pos & STBDS_BUCKET_MASK;
-            for (z = 0; z < limit; ++z) {
-              if (bucket->hash[z] == 0) {
-                bucket->hash[z] = hash;
-                bucket->index[z] = ob->index[j];
-                goto done;
-              }
-            }
-
-            pos += step;                  // quadratic probing
-            step += STBDS_BUCKET_LENGTH;
-            pos &= (t->slot_count-1);
-          }
-        }
-       done:
-        ;
-      }
-    }
-  }
-
-  return t;
-}
-
-#define STBDS_ROTATE_LEFT(val, n)   (((val) << (n)) | ((val) >> (STBDS_SIZE_T_BITS - (n))))
-#define STBDS_ROTATE_RIGHT(val, n)  (((val) >> (n)) | ((val) << (STBDS_SIZE_T_BITS - (n))))
-
-size_t stbds_hash_string(char *str, size_t seed)
-{
-  size_t hash = seed;
-  while (*str)
-     hash = STBDS_ROTATE_LEFT(hash, 9) + (unsigned char) *str++;
-
-  // Thomas Wang 64-to-32 bit mix function, hopefully also works in 32 bits
-  hash ^= seed;
-  hash = (~hash) + (hash << 18);
-  hash ^= hash ^ STBDS_ROTATE_RIGHT(hash,31);
-  hash = hash * 21;
-  hash ^= hash ^ STBDS_ROTATE_RIGHT(hash,11);
-  hash += (hash << 6);
-  hash ^= STBDS_ROTATE_RIGHT(hash,22);
-  return hash+seed;
-}
-
-#ifdef STBDS_SIPHASH_2_4
-#define STBDS_SIPHASH_C_ROUNDS 2
-#define STBDS_SIPHASH_D_ROUNDS 4
-typedef int STBDS_SIPHASH_2_4_can_only_be_used_in_64_bit_builds[sizeof(size_t) == 8 ? 1 : -1];
-#endif
-
-#ifndef STBDS_SIPHASH_C_ROUNDS
-#define STBDS_SIPHASH_C_ROUNDS 1
-#endif
-#ifndef STBDS_SIPHASH_D_ROUNDS
-#define STBDS_SIPHASH_D_ROUNDS 1
-#endif
-
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable:4127) // conditional expression is constant, for do..while(0) and sizeof()==
-#endif
-
-static size_t stbds_siphash_bytes(void *p, size_t len, size_t seed)
-{
-  unsigned char *d = (unsigned char *) p;
-  size_t i,j;
-  size_t v0,v1,v2,v3, data;
-
-  // hash that works on 32- or 64-bit registers without knowing which we have
-  // (computes different results on 32-bit and 64-bit platform)
-  // derived from siphash, but on 32-bit platforms very different as it uses 4 32-bit state not 4 64-bit
-  v0 = ((((size_t) 0x736f6d65 << 16) << 16) + 0x70736575) ^  seed;
-  v1 = ((((size_t) 0x646f7261 << 16) << 16) + 0x6e646f6d) ^ ~seed;
-  v2 = ((((size_t) 0x6c796765 << 16) << 16) + 0x6e657261) ^  seed;
-  v3 = ((((size_t) 0x74656462 << 16) << 16) + 0x79746573) ^ ~seed;
-
-  #ifdef STBDS_TEST_SIPHASH_2_4
-  // hardcoded with key material in the siphash test vectors
-  v0 ^= 0x0706050403020100ull ^  seed;
-  v1 ^= 0x0f0e0d0c0b0a0908ull ^ ~seed;
-  v2 ^= 0x0706050403020100ull ^  seed;
-  v3 ^= 0x0f0e0d0c0b0a0908ull ^ ~seed;
-  #endif
-
-  #define STBDS_SIPROUND() \
-    do {                   \
-      v0 += v1; v1 = STBDS_ROTATE_LEFT(v1, 13);  v1 ^= v0; v0 = STBDS_ROTATE_LEFT(v0,STBDS_SIZE_T_BITS/2); \
-      v2 += v3; v3 = STBDS_ROTATE_LEFT(v3, 16);  v3 ^= v2;                                                 \
-      v2 += v1; v1 = STBDS_ROTATE_LEFT(v1, 17);  v1 ^= v2; v2 = STBDS_ROTATE_LEFT(v2,STBDS_SIZE_T_BITS/2); \
-      v0 += v3; v3 = STBDS_ROTATE_LEFT(v3, 21);  v3 ^= v0;                                                 \
-    } while (0)
-
-  for (i=0; i+sizeof(size_t) <= len; i += sizeof(size_t), d += sizeof(size_t)) {
-    data = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);
-    data |= (size_t) (d[4] | (d[5] << 8) | (d[6] << 16) | (d[7] << 24)) << 16 << 16; // discarded if size_t == 4
-
-    v3 ^= data;
-    for (j=0; j < STBDS_SIPHASH_C_ROUNDS; ++j)
-      STBDS_SIPROUND();
-    v0 ^= data;
-  }
-  data = len << (STBDS_SIZE_T_BITS-8);
-  switch (len - i) {
-    case 7: data |= ((size_t) d[6] << 24) << 24; // fall through
-    case 6: data |= ((size_t) d[5] << 20) << 20; // fall through
-    case 5: data |= ((size_t) d[4] << 16) << 16; // fall through
-    case 4: data |= (d[3] << 24); // fall through
-    case 3: data |= (d[2] << 16); // fall through
-    case 2: data |= (d[1] << 8); // fall through
-    case 1: data |= d[0]; // fall through
-    case 0: break;
-  }
-  v3 ^= data;
-  for (j=0; j < STBDS_SIPHASH_C_ROUNDS; ++j)
-    STBDS_SIPROUND();
-  v0 ^= data;
-  v2 ^= 0xff;
-  for (j=0; j < STBDS_SIPHASH_D_ROUNDS; ++j)
-    STBDS_SIPROUND();
-
-#ifdef STBDS_SIPHASH_2_4
-  return v0^v1^v2^v3;
-#else
-  return v1^v2^v3; // slightly stronger since v0^v3 in above cancels out final round operation? I tweeted at the authors of SipHash about this but they didn't reply
-#endif
-}
-
-size_t stbds_hash_bytes(void *p, size_t len, size_t seed)
-{
-#ifdef STBDS_SIPHASH_2_4
-  return stbds_siphash_bytes(p,len,seed);
-#else
-  unsigned char *d = (unsigned char *) p;
-
-  if (len == 4) {
-    unsigned int hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);
-    #if 0
-    // HASH32-A  Bob Jenkin's hash function w/o large constants
-    hash ^= seed;
-    hash -= (hash<<6);
-    hash ^= (hash>>17);
-    hash -= (hash<<9);
-    hash ^= seed;
-    hash ^= (hash<<4);
-    hash -= (hash<<3);
-    hash ^= (hash<<10);
-    hash ^= (hash>>15);
-    #elif 1
-    // HASH32-BB  Bob Jenkin's presumably-accidental version of Thomas Wang hash with rotates turned into shifts.
-    // Note that converting these back to rotates makes it run a lot slower, presumably due to collisions, so I'm
-    // not really sure what's going on.
-    hash ^= seed;
-    hash = (hash ^ 61) ^ (hash >> 16);
-    hash = hash + (hash << 3);
-    hash = hash ^ (hash >> 4);
-    hash = hash * 0x27d4eb2d;
-    hash ^= seed;
-    hash = hash ^ (hash >> 15);
-    #else  // HASH32-C   -  Murmur3
-    hash ^= seed;
-    hash *= 0xcc9e2d51;
-    hash = (hash << 17) | (hash >> 15);
-    hash *= 0x1b873593;
-    hash ^= seed;
-    hash = (hash << 19) | (hash >> 13);
-    hash = hash*5 + 0xe6546b64;
-    hash ^= hash >> 16;
-    hash *= 0x85ebca6b;
-    hash ^= seed;
-    hash ^= hash >> 13;
-    hash *= 0xc2b2ae35;
-    hash ^= hash >> 16;
-    #endif
-    // Following statistics were measured on a Core i7-6700 @ 4.00Ghz, compiled with clang 7.0.1 -O2
-    // Note that the larger tables have high variance as they were run fewer times
-    //  HASH32-A   //  HASH32-BB  //  HASH32-C
-    //    0.10ms   //    0.10ms   //    0.10ms :      2,000 inserts creating 2K table
-    //    0.96ms   //    0.95ms   //    0.99ms :     20,000 inserts creating 20K table
-    //   14.69ms   //   14.43ms   //   14.97ms :    200,000 inserts creating 200K table
-    //  199.99ms   //  195.36ms   //  202.05ms :  2,000,000 inserts creating 2M table
-    // 2234.84ms   // 2187.74ms   // 2240.38ms : 20,000,000 inserts creating 20M table
-    //   55.68ms   //   53.72ms   //   57.31ms : 500,000 inserts & deletes in 2K table
-    //   63.43ms   //   61.99ms   //   65.73ms : 500,000 inserts & deletes in 20K table
-    //   80.04ms   //   77.96ms   //   81.83ms : 500,000 inserts & deletes in 200K table
-    //  100.42ms   //   97.40ms   //  102.39ms : 500,000 inserts & deletes in 2M table
-    //  119.71ms   //  120.59ms   //  121.63ms : 500,000 inserts & deletes in 20M table
-    //  185.28ms   //  195.15ms   //  187.74ms : 500,000 inserts & deletes in 200M table
-    //   15.58ms   //   14.79ms   //   15.52ms : 200,000 inserts creating 200K table with varying key spacing
-
-    return (((size_t) hash << 16 << 16) | hash) ^ seed;
-  } else if (len == 8 && sizeof(size_t) == 8) {
-    size_t hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);
-    hash |= (size_t) (d[4] | (d[5] << 8) | (d[6] << 16) | (d[7] << 24)) << 16 << 16; // avoid warning if size_t == 4
-    hash ^= seed;
-    hash = (~hash) + (hash << 21);
-    hash ^= STBDS_ROTATE_RIGHT(hash,24);
-    hash *= 265;
-    hash ^= STBDS_ROTATE_RIGHT(hash,14);
-    hash ^= seed;
-    hash *= 21;
-    hash ^= STBDS_ROTATE_RIGHT(hash,28);
-    hash += (hash << 31);
-    hash = (~hash) + (hash << 18);
-    return hash;
-  } else {
-    return stbds_siphash_bytes(p,len,seed);
-  }
-#endif
-}
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif
-
-
-static int stbds_is_key_equal(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode, size_t i)
-{
-  if (mode >= STBDS_HM_STRING)
-    return 0==strcmp((char *) key, * (char **) ((char *) a + elemsize*i + keyoffset));
-  else
-    return 0==memcmp(key, (char *) a + elemsize*i + keyoffset, keysize);
-}
-
-#define STBDS_HASH_TO_ARR(x,elemsize) ((char*) (x) - (elemsize))
-#define STBDS_ARR_TO_HASH(x,elemsize) ((char*) (x) + (elemsize))
-
-#define stbds_hash_table(a)  ((stbds_hash_index *) stbds_header(a)->hash_table)
-
-void stbds_hmfree_func(void *a, size_t elemsize)
-{
-  if (a == NULL) return;
-  if (stbds_hash_table(a) != NULL) {
-    if (stbds_hash_table(a)->string.mode == STBDS_SH_STRDUP) {
-      size_t i;
-      // skip 0th element, which is default
-      for (i=1; i < stbds_header(a)->length; ++i)
-        STBDS_FREE(NULL, *(char**) ((char *) a + elemsize*i));
-    }
-    stbds_strreset(&stbds_hash_table(a)->string);
-  }
-  STBDS_FREE(NULL, stbds_header(a)->hash_table);
-  STBDS_FREE(NULL, stbds_header(a));
-}
-
-static ptrdiff_t stbds_hm_find_slot(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode)
-{
-  void *raw_a = STBDS_HASH_TO_ARR(a,elemsize);
-  stbds_hash_index *table = stbds_hash_table(raw_a);
-  size_t hash = mode >= STBDS_HM_STRING ? stbds_hash_string((char*)key,table->seed) : stbds_hash_bytes(key, keysize,table->seed);
-  size_t step = STBDS_BUCKET_LENGTH;
-  size_t limit,i;
-  size_t pos;
-  stbds_hash_bucket *bucket;
-
-  if (hash < 2) hash += 2; // stored hash values are forbidden from being 0, so we can detect empty slots
-
-  pos = stbds_probe_position(hash, table->slot_count, table->slot_count_log2);
-
-  for (;;) {
-    STBDS_STATS(++stbds_hash_probes);
-    bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT];
-
-    // start searching from pos to end of bucket, this should help performance on small hash tables that fit in cache
-    for (i=pos & STBDS_BUCKET_MASK; i < STBDS_BUCKET_LENGTH; ++i) {
-      if (bucket->hash[i] == hash) {
-        if (stbds_is_key_equal(a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) {
-          return (pos & ~STBDS_BUCKET_MASK)+i;
-        }
-      } else if (bucket->hash[i] == STBDS_HASH_EMPTY) {
-        return -1;
-      }
-    }
-
-    // search from beginning of bucket to pos
-    limit = pos & STBDS_BUCKET_MASK;
-    for (i = 0; i < limit; ++i) {
-      if (bucket->hash[i] == hash) {
-        if (stbds_is_key_equal(a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) {
-          return (pos & ~STBDS_BUCKET_MASK)+i;
-        }
-      } else if (bucket->hash[i] == STBDS_HASH_EMPTY) {
-        return -1;
-      }
-    }
-
-    // quadratic probing
-    pos += step;
-    step += STBDS_BUCKET_LENGTH;
-    pos &= (table->slot_count-1);
-  }
-  /* NOTREACHED */
-}
-
-void * stbds_hmget_key_ts(void *a, size_t elemsize, void *key, size_t keysize, ptrdiff_t *temp, int mode)
-{
-  size_t keyoffset = 0;
-  if (a == NULL) {
-    // make it non-empty so we can return a temp
-    a = stbds_arrgrowf(0, elemsize, 0, 1);
-    stbds_header(a)->length += 1;
-    memset(a, 0, elemsize);
-    *temp = STBDS_INDEX_EMPTY;
-    // adjust a to point after the default element
-    return STBDS_ARR_TO_HASH(a,elemsize);
-  } else {
-    stbds_hash_index *table;
-    void *raw_a = STBDS_HASH_TO_ARR(a,elemsize);
-    // adjust a to point to the default element
-    table = (stbds_hash_index *) stbds_header(raw_a)->hash_table;
-    if (table == 0) {
-      *temp = -1;
-    } else {
-      ptrdiff_t slot = stbds_hm_find_slot(a, elemsize, key, keysize, keyoffset, mode);
-      if (slot < 0) {
-        *temp = STBDS_INDEX_EMPTY;
-      } else {
-        stbds_hash_bucket *b = &table->storage[slot >> STBDS_BUCKET_SHIFT];
-        *temp = b->index[slot & STBDS_BUCKET_MASK];
-      }
-    }
-    return a;
-  }
-}
-
-void * stbds_hmget_key(void *a, size_t elemsize, void *key, size_t keysize, int mode)
-{
-  ptrdiff_t temp;
-  void *p = stbds_hmget_key_ts(a, elemsize, key, keysize, &temp, mode);
-  stbds_temp(STBDS_HASH_TO_ARR(p,elemsize)) = temp;
-  return p;
-}
-
-void * stbds_hmput_default(void *a, size_t elemsize)
-{
-  // three cases:
-  //   a is NULL <- allocate
-  //   a has a hash table but no entries, because of shmode <- grow
-  //   a has entries <- do nothing
-  if (a == NULL || stbds_header(STBDS_HASH_TO_ARR(a,elemsize))->length == 0) {
-    a = stbds_arrgrowf(a ? STBDS_HASH_TO_ARR(a,elemsize) : NULL, elemsize, 0, 1);
-    stbds_header(a)->length += 1;
-    memset(a, 0, elemsize);
-    a=STBDS_ARR_TO_HASH(a,elemsize);
-  }
-  return a;
-}
-
-static char *stbds_strdup(char *str);
-
-void *stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, int mode)
-{
-  size_t keyoffset=0;
-  void *raw_a;
-  stbds_hash_index *table;
-
-  if (a == NULL) {
-    a = stbds_arrgrowf(0, elemsize, 0, 1);
-    memset(a, 0, elemsize);
-    stbds_header(a)->length += 1;
-    // adjust a to point AFTER the default element
-    a = STBDS_ARR_TO_HASH(a,elemsize);
-  }
-
-  // adjust a to point to the default element
-  raw_a = a;
-  a = STBDS_HASH_TO_ARR(a,elemsize);
-
-  table = (stbds_hash_index *) stbds_header(a)->hash_table;
-
-  if (table == NULL || table->used_count >= table->used_count_threshold) {
-    stbds_hash_index *nt;
-    size_t slot_count;
-
-    slot_count = (table == NULL) ? STBDS_BUCKET_LENGTH : table->slot_count*2;
-    nt = stbds_make_hash_index(slot_count, table);
-    if (table)
-      STBDS_FREE(NULL, table);
-    else
-      nt->string.mode = mode >= STBDS_HM_STRING ? STBDS_SH_DEFAULT : 0;
-    stbds_header(a)->hash_table = table = nt;
-    STBDS_STATS(++stbds_hash_grow);
-  }
-
-  // we iterate hash table explicitly because we want to track if we saw a tombstone
-  {
-    size_t hash = mode >= STBDS_HM_STRING ? stbds_hash_string((char*)key,table->seed) : stbds_hash_bytes(key, keysize,table->seed);
-    size_t step = STBDS_BUCKET_LENGTH;
-    size_t pos;
-    ptrdiff_t tombstone = -1;
-    stbds_hash_bucket *bucket;
-
-    // stored hash values are forbidden from being 0, so we can detect empty slots to early out quickly
-    if (hash < 2) hash += 2;
-
-    pos = stbds_probe_position(hash, table->slot_count, table->slot_count_log2);
-
-    for (;;) {
-      size_t limit, i;
-      STBDS_STATS(++stbds_hash_probes);
-      bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT];
-
-      // start searching from pos to end of bucket
-      for (i=pos & STBDS_BUCKET_MASK; i < STBDS_BUCKET_LENGTH; ++i) {
-        if (bucket->hash[i] == hash) {
-          if (stbds_is_key_equal(raw_a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) {
-            stbds_temp(a) = bucket->index[i];
-            if (mode >= STBDS_HM_STRING)
-              stbds_temp_key(a) = * (char **) ((char *) raw_a + elemsize*bucket->index[i] + keyoffset);
-            return STBDS_ARR_TO_HASH(a,elemsize);
-          }
-        } else if (bucket->hash[i] == 0) {
-          pos = (pos & ~STBDS_BUCKET_MASK) + i;
-          goto found_empty_slot;
-        } else if (tombstone < 0) {
-          if (bucket->index[i] == STBDS_INDEX_DELETED)
-            tombstone = (ptrdiff_t) ((pos & ~STBDS_BUCKET_MASK) + i);
-        }
-      }
-
-      // search from beginning of bucket to pos
-      limit = pos & STBDS_BUCKET_MASK;
-      for (i = 0; i < limit; ++i) {
-        if (bucket->hash[i] == hash) {
-          if (stbds_is_key_equal(raw_a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) {
-            stbds_temp(a) = bucket->index[i];
-            return STBDS_ARR_TO_HASH(a,elemsize);
-          }
-        } else if (bucket->hash[i] == 0) {
-          pos = (pos & ~STBDS_BUCKET_MASK) + i;
-          goto found_empty_slot;
-        } else if (tombstone < 0) {
-          if (bucket->index[i] == STBDS_INDEX_DELETED)
-            tombstone = (ptrdiff_t) ((pos & ~STBDS_BUCKET_MASK) + i);
-        }
-      }
-
-      // quadratic probing
-      pos += step;
-      step += STBDS_BUCKET_LENGTH;
-      pos &= (table->slot_count-1);
-    }
-   found_empty_slot:
-    if (tombstone >= 0) {
-      pos = tombstone;
-      --table->tombstone_count;
-    }
-    ++table->used_count;
-
-    {
-      ptrdiff_t i = (ptrdiff_t) stbds_arrlen(a);
-      // we want to do stbds_arraddn(1), but we can't use the macros since we don't have something of the right type
-      if ((size_t) i+1 > stbds_arrcap(a))
-        *(void **) &a = stbds_arrgrowf(a, elemsize, 1, 0);
-      raw_a = STBDS_ARR_TO_HASH(a,elemsize);
-
-      STBDS_ASSERT((size_t) i+1 <= stbds_arrcap(a));
-      stbds_header(a)->length = i+1;
-      bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT];
-      bucket->hash[pos & STBDS_BUCKET_MASK] = hash;
-      bucket->index[pos & STBDS_BUCKET_MASK] = i-1;
-      stbds_temp(a) = i-1;
-
-      switch (table->string.mode) {
-         case STBDS_SH_STRDUP:  stbds_temp_key(a) = *(char **) ((char *) a + elemsize*i) = stbds_strdup((char*) key); break;
-         case STBDS_SH_ARENA:   stbds_temp_key(a) = *(char **) ((char *) a + elemsize*i) = stbds_stralloc(&table->string, (char*)key); break;
-         case STBDS_SH_DEFAULT: stbds_temp_key(a) = *(char **) ((char *) a + elemsize*i) = (char *) key; break;
-         default:                memcpy((char *) a + elemsize*i, key, keysize); break;
-      }
-    }
-    return STBDS_ARR_TO_HASH(a,elemsize);
-  }
-}
-
-void * stbds_shmode_func(size_t elemsize, int mode)
-{
-  void *a = stbds_arrgrowf(0, elemsize, 0, 1);
-  stbds_hash_index *h;
-  memset(a, 0, elemsize);
-  stbds_header(a)->length = 1;
-  stbds_header(a)->hash_table = h = (stbds_hash_index *) stbds_make_hash_index(STBDS_BUCKET_LENGTH, NULL);
-  h->string.mode = (unsigned char) mode;
-  return STBDS_ARR_TO_HASH(a,elemsize);
-}
-
-void * stbds_hmdel_key(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode)
-{
-  if (a == NULL) {
-    return 0;
-  } else {
-    stbds_hash_index *table;
-    void *raw_a = STBDS_HASH_TO_ARR(a,elemsize);
-    table = (stbds_hash_index *) stbds_header(raw_a)->hash_table;
-    stbds_temp(raw_a) = 0;
-    if (table == 0) {
-      return a;
-    } else {
-      ptrdiff_t slot;
-      slot = stbds_hm_find_slot(a, elemsize, key, keysize, keyoffset, mode);
-      if (slot < 0)
-        return a;
-      else {
-        stbds_hash_bucket *b = &table->storage[slot >> STBDS_BUCKET_SHIFT];
-        int i = slot & STBDS_BUCKET_MASK;
-        ptrdiff_t old_index = b->index[i];
-        ptrdiff_t final_index = (ptrdiff_t) stbds_arrlen(raw_a)-1-1; // minus one for the raw_a vs a, and minus one for 'last'
-        STBDS_ASSERT(slot < (ptrdiff_t) table->slot_count);
-        --table->used_count;
-        ++table->tombstone_count;
-        stbds_temp(raw_a) = 1;
-        STBDS_ASSERT(table->used_count >= 0);
-        //STBDS_ASSERT(table->tombstone_count < table->slot_count/4);
-        b->hash[i] = STBDS_HASH_DELETED;
-        b->index[i] = STBDS_INDEX_DELETED;
-
-        if (mode == STBDS_HM_STRING && table->string.mode == STBDS_SH_STRDUP)
-          STBDS_FREE(NULL, *(char**) ((char *) a+elemsize*old_index));
-
-        // if indices are the same, memcpy is a no-op, but back-pointer-fixup will fail, so skip
-        if (old_index != final_index) {
-          // swap delete
-          memmove((char*) a + elemsize*old_index, (char*) a + elemsize*final_index, elemsize);
-
-          // now find the slot for the last element
-          if (mode == STBDS_HM_STRING)
-            slot = stbds_hm_find_slot(a, elemsize, *(char**) ((char *) a+elemsize*old_index + keyoffset), keysize, keyoffset, mode);
-          else
-            slot = stbds_hm_find_slot(a, elemsize,  (char* ) a+elemsize*old_index + keyoffset, keysize, keyoffset, mode);
-          STBDS_ASSERT(slot >= 0);
-          b = &table->storage[slot >> STBDS_BUCKET_SHIFT];
-          i = slot & STBDS_BUCKET_MASK;
-          STBDS_ASSERT(b->index[i] == final_index);
-          b->index[i] = old_index;
-        }
-        stbds_header(raw_a)->length -= 1;
-
-        if (table->used_count < table->used_count_shrink_threshold && table->slot_count > STBDS_BUCKET_LENGTH) {
-          stbds_header(raw_a)->hash_table = stbds_make_hash_index(table->slot_count>>1, table);
-          STBDS_FREE(NULL, table);
-          STBDS_STATS(++stbds_hash_shrink);
-        } else if (table->tombstone_count > table->tombstone_count_threshold) {
-          stbds_header(raw_a)->hash_table = stbds_make_hash_index(table->slot_count   , table);
-          STBDS_FREE(NULL, table);
-          STBDS_STATS(++stbds_hash_rebuild);
-        }
-
-        return a;
-      }
-    }
-  }
-  /* NOTREACHED */
-}
-
-static char *stbds_strdup(char *str)
-{
-  // to keep replaceable allocator simple, we don't want to use strdup.
-  // rolling our own also avoids problem of strdup vs _strdup
-  size_t len = strlen(str)+1;
-  char *p = (char*) STBDS_REALLOC(NULL, 0, len);
-  memmove(p, str, len);
-  return p;
-}
-
-#ifndef STBDS_STRING_ARENA_BLOCKSIZE_MIN
-#define STBDS_STRING_ARENA_BLOCKSIZE_MIN  512u
-#endif
-#ifndef STBDS_STRING_ARENA_BLOCKSIZE_MAX
-#define STBDS_STRING_ARENA_BLOCKSIZE_MAX  (1u<<20)
-#endif
-
-char *stbds_stralloc(stbds_string_arena *a, char *str)
-{
-  char *p;
-  size_t len = strlen(str)+1;
-  if (len > a->remaining) {
-    // compute the next blocksize
-    size_t blocksize = a->block;
-
-    // size is 512, 512, 1024, 1024, 2048, 2048, 4096, 4096, etc., so that
-    // there are log(SIZE) allocations to free when we destroy the table
-    blocksize = (size_t) (STBDS_STRING_ARENA_BLOCKSIZE_MIN) << (blocksize>>1);
-
-    // if size is under 1M, advance to next blocktype
-    if (blocksize < (size_t)(STBDS_STRING_ARENA_BLOCKSIZE_MAX))
-      ++a->block;
-
-    if (len > blocksize) {
-      // if string is larger than blocksize, then just allocate the full size.
-      // note that we still advance string_block so block size will continue
-      // increasing, so e.g. if somebody only calls this with 1000-long strings,
-      // eventually the arena will start doubling and handling those as well
-      stbds_string_block *sb = (stbds_string_block *) STBDS_REALLOC(NULL, 0, sizeof(*sb)-8 + len);
-      memmove(sb->storage, str, len);
-      if (a->storage) {
-        // insert it after the first element, so that we don't waste the space there
-        sb->next = a->storage->next;
-        a->storage->next = sb;
-      } else {
-        sb->next = 0;
-        a->storage = sb;
-        a->remaining = 0; // this is redundant, but good for clarity
-      }
-      return sb->storage;
-    } else {
-      stbds_string_block *sb = (stbds_string_block *) STBDS_REALLOC(NULL, 0, sizeof(*sb)-8 + blocksize);
-      sb->next = a->storage;
-      a->storage = sb;
-      a->remaining = blocksize;
-    }
-  }
-
-  STBDS_ASSERT(len <= a->remaining);
-  p = a->storage->storage + a->remaining - len;
-  a->remaining -= len;
-  memmove(p, str, len);
-  return p;
-}
-
-void stbds_strreset(stbds_string_arena *a)
-{
-  stbds_string_block *x,*y;
-  x = a->storage;
-  while (x) {
-    y = x->next;
-    STBDS_FREE(NULL, x);
-    x = y;
-  }
-  memset(a, 0, sizeof(*a));
-}
-
-#endif
-
-//////////////////////////////////////////////////////////////////////////////
-//
-//   UNIT TESTS
-//
-
-#ifdef STBDS_UNIT_TESTS
-#include <stdio.h>
-#ifdef STBDS_ASSERT_WAS_UNDEFINED
-#undef STBDS_ASSERT
-#endif
-#ifndef STBDS_ASSERT
-#define STBDS_ASSERT assert
-#include <assert.h>
-#endif
-
-typedef struct { int key,b,c,d; } stbds_struct;
-typedef struct { int key[2],b,c,d; } stbds_struct2;
-
-static char buffer[256];
-char *strkey(int n)
-{
-#if defined(_WIN32) && defined(__STDC_WANT_SECURE_LIB__)
-   sprintf_s(buffer, sizeof(buffer), "test_%d", n);
-#else
-   sprintf(buffer, "test_%d", n);
-#endif
-   return buffer;
-}
-
-void stbds_unit_tests(void)
-{
-#if defined(_MSC_VER) && _MSC_VER <= 1200 && defined(__cplusplus)
-  // VC6 C++ doesn't like the template<> trick on unnamed structures, so do nothing!
-  STBDS_ASSERT(0);
-#else
-  const int testsize = 100000;
-  const int testsize2 = testsize/20;
-  int *arr=NULL;
-  struct { int   key;        int value; }  *intmap  = NULL;
-  struct { char *key;        int value; }  *strmap  = NULL, s;
-  struct { stbds_struct key; int value; }  *map     = NULL;
-  stbds_struct                             *map2    = NULL;
-  stbds_struct2                            *map3    = NULL;
-  stbds_string_arena                        sa      = { 0 };
-  int key3[2] = { 1,2 };
-  ptrdiff_t temp;
-
-  int i,j;
-
-  STBDS_ASSERT(arrlen(arr)==0);
-  for (i=0; i < 20000; i += 50) {
-    for (j=0; j < i; ++j)
-      arrpush(arr,j);
-    arrfree(arr);
-  }
-
-  for (i=0; i < 4; ++i) {
-    arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4);
-    arrdel(arr,i);
-    arrfree(arr);
-    arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4);
-    arrdelswap(arr,i);
-    arrfree(arr);
-  }
-
-  for (i=0; i < 5; ++i) {
-    arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4);
-    stbds_arrins(arr,i,5);
-    STBDS_ASSERT(arr[i] == 5);
-    if (i < 4)
-      STBDS_ASSERT(arr[4] == 4);
-    arrfree(arr);
-  }
-
-  i = 1;
-  STBDS_ASSERT(hmgeti(intmap,i) == -1);
-  hmdefault(intmap, -2);
-  STBDS_ASSERT(hmgeti(intmap, i) == -1);
-  STBDS_ASSERT(hmget (intmap, i) == -2);
-  for (i=0; i < testsize; i+=2)
-    hmput(intmap, i, i*5);
-  for (i=0; i < testsize; i+=1) {
-    if (i & 1) STBDS_ASSERT(hmget(intmap, i) == -2 );
-    else       STBDS_ASSERT(hmget(intmap, i) == i*5);
-    if (i & 1) STBDS_ASSERT(hmget_ts(intmap, i, temp) == -2 );
-    else       STBDS_ASSERT(hmget_ts(intmap, i, temp) == i*5);
-  }
-  for (i=0; i < testsize; i+=2)
-    hmput(intmap, i, i*3);
-  for (i=0; i < testsize; i+=1)
-    if (i & 1) STBDS_ASSERT(hmget(intmap, i) == -2 );
-    else       STBDS_ASSERT(hmget(intmap, i) == i*3);
-  for (i=2; i < testsize; i+=4)
-    hmdel(intmap, i); // delete half the entries
-  for (i=0; i < testsize; i+=1)
-    if (i & 3) STBDS_ASSERT(hmget(intmap, i) == -2 );
-    else       STBDS_ASSERT(hmget(intmap, i) == i*3);
-  for (i=0; i < testsize; i+=1)
-    hmdel(intmap, i); // delete the rest of the entries
-  for (i=0; i < testsize; i+=1)
-    STBDS_ASSERT(hmget(intmap, i) == -2 );
-  hmfree(intmap);
-  for (i=0; i < testsize; i+=2)
-    hmput(intmap, i, i*3);
-  hmfree(intmap);
-
-  #if defined(__clang__) || defined(__GNUC__)
-  #ifndef __cplusplus
-  intmap = NULL;
-  hmput(intmap, 15, 7);
-  hmput(intmap, 11, 3);
-  hmput(intmap,  9, 5);
-  STBDS_ASSERT(hmget(intmap, 9) == 5);
-  STBDS_ASSERT(hmget(intmap, 11) == 3);
-  STBDS_ASSERT(hmget(intmap, 15) == 7);
-  #endif
-  #endif
-
-  for (i=0; i < testsize; ++i)
-    stralloc(&sa, strkey(i));
-  strreset(&sa);
-
-  {
-    s.key = "a", s.value = 1;
-    shputs(strmap, s);
-    STBDS_ASSERT(*strmap[0].key == 'a');
-    STBDS_ASSERT(strmap[0].key == s.key);
-    STBDS_ASSERT(strmap[0].value == s.value);
-    shfree(strmap);
-  }
-
-  {
-    s.key = "a", s.value = 1;
-    sh_new_strdup(strmap);
-    shputs(strmap, s);
-    STBDS_ASSERT(*strmap[0].key == 'a');
-    STBDS_ASSERT(strmap[0].key != s.key);
-    STBDS_ASSERT(strmap[0].value == s.value);
-    shfree(strmap);
-  }
-
-  {
-    s.key = "a", s.value = 1;
-    sh_new_arena(strmap);
-    shputs(strmap, s);
-    STBDS_ASSERT(*strmap[0].key == 'a');
-    STBDS_ASSERT(strmap[0].key != s.key);
-    STBDS_ASSERT(strmap[0].value == s.value);
-    shfree(strmap);
-  }
-
-  for (j=0; j < 2; ++j) {
-    STBDS_ASSERT(shgeti(strmap,"foo") == -1);
-    if (j == 0)
-      sh_new_strdup(strmap);
-    else
-      sh_new_arena(strmap);
-    STBDS_ASSERT(shgeti(strmap,"foo") == -1);
-    shdefault(strmap, -2);
-    STBDS_ASSERT(shgeti(strmap,"foo") == -1);
-    for (i=0; i < testsize; i+=2)
-      shput(strmap, strkey(i), i*3);
-    for (i=0; i < testsize; i+=1)
-      if (i & 1) STBDS_ASSERT(shget(strmap, strkey(i)) == -2 );
-      else       STBDS_ASSERT(shget(strmap, strkey(i)) == i*3);
-    for (i=2; i < testsize; i+=4)
-      shdel(strmap, strkey(i)); // delete half the entries
-    for (i=0; i < testsize; i+=1)
-      if (i & 3) STBDS_ASSERT(shget(strmap, strkey(i)) == -2 );
-      else       STBDS_ASSERT(shget(strmap, strkey(i)) == i*3);
-    for (i=0; i < testsize; i+=1)
-      shdel(strmap, strkey(i)); // delete the rest of the entries
-    for (i=0; i < testsize; i+=1)
-      STBDS_ASSERT(shget(strmap, strkey(i)) == -2 );
-    shfree(strmap);
-  }
-
-  {
-    struct { char *key; char value; } *hash = NULL;
-    char name[4] = "jen";
-    shput(hash, "bob"   , 'h');
-    shput(hash, "sally" , 'e');
-    shput(hash, "fred"  , 'l');
-    shput(hash, "jen"   , 'x');
-    shput(hash, "doug"  , 'o');
-
-    shput(hash, name    , 'l');
-    shfree(hash);
-  }
-
-  for (i=0; i < testsize; i += 2) {
-    stbds_struct s = { i,i*2,i*3,i*4 };
-    hmput(map, s, i*5);
-  }
-
-  for (i=0; i < testsize; i += 1) {
-    stbds_struct s = { i,i*2,i*3  ,i*4 };
-    stbds_struct t = { i,i*2,i*3+1,i*4 };
-    if (i & 1) STBDS_ASSERT(hmget(map, s) == 0);
-    else       STBDS_ASSERT(hmget(map, s) == i*5);
-    if (i & 1) STBDS_ASSERT(hmget_ts(map, s, temp) == 0);
-    else       STBDS_ASSERT(hmget_ts(map, s, temp) == i*5);
-    //STBDS_ASSERT(hmget(map, t.key) == 0);
-  }
-
-  for (i=0; i < testsize; i += 2) {
-    stbds_struct s = { i,i*2,i*3,i*4 };
-    hmputs(map2, s);
-  }
-  hmfree(map);
-
-  for (i=0; i < testsize; i += 1) {
-    stbds_struct s = { i,i*2,i*3,i*4 };
-    stbds_struct t = { i,i*2,i*3+1,i*4 };
-    if (i & 1) STBDS_ASSERT(hmgets(map2, s.key).d == 0);
-    else       STBDS_ASSERT(hmgets(map2, s.key).d == i*4);
-    //STBDS_ASSERT(hmgetp(map2, t.key) == 0);
-  }
-  hmfree(map2);
-
-  for (i=0; i < testsize; i += 2) {
-    stbds_struct2 s = { { i,i*2 }, i*3,i*4, i*5 };
-    hmputs(map3, s);
-  }
-  for (i=0; i < testsize; i += 1) {
-    stbds_struct2 s = { { i,i*2}, i*3, i*4, i*5 };
-    stbds_struct2 t = { { i,i*2}, i*3+1, i*4, i*5 };
-    if (i & 1) STBDS_ASSERT(hmgets(map3, s.key).d == 0);
-    else       STBDS_ASSERT(hmgets(map3, s.key).d == i*5);
-    //STBDS_ASSERT(hmgetp(map3, t.key) == 0);
-  }
-#endif
-}
-#endif
-
-
-/*
-------------------------------------------------------------------------------
-This software is available under 2 licenses -- choose whichever you prefer.
-------------------------------------------------------------------------------
-ALTERNATIVE A - MIT License
-Copyright (c) 2019 Sean Barrett
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is furnished to do
-so, subject to the following conditions:
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-------------------------------------------------------------------------------
-ALTERNATIVE B - Public Domain (www.unlicense.org)
-This is free and unencumbered software released into the public domain.
-Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
-software, either in source code form or as a compiled binary, for any purpose,
-commercial or non-commercial, and by any means.
-In jurisdictions that recognize copyright laws, the author or authors of this
-software dedicate any and all copyright interest in the software to the public
-domain. We make this dedication for the benefit of the public at large and to
-the detriment of our heirs and successors. We intend this dedication to be an
-overt act of relinquishment in perpetuity of all present and future rights to
-this software under copyright law.
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-------------------------------------------------------------------------------
-*/
diff --git a/include/types.h b/include/types.h
deleted file mode 100644 (file)
index 5c2b5e6..0000000
+++ /dev/null
@@ -1,247 +0,0 @@
-#ifndef ONYXTYPES_H
-#define ONYXTYPES_H
-
-#include "bh.h"
-
-#define POINTER_SIZE 4
-
-enum BasicKind {
-    Basic_Kind_Void,
-
-    Basic_Kind_Bool,
-
-    Basic_Kind_Int_Unsized,
-    Basic_Kind_I8,
-    Basic_Kind_U8,
-    Basic_Kind_I16,
-    Basic_Kind_U16,
-    Basic_Kind_I32,
-    Basic_Kind_U32,
-    Basic_Kind_I64,
-    Basic_Kind_U64,
-
-    Basic_Kind_Float_Unsized,
-    Basic_Kind_F32,
-    Basic_Kind_F64,
-
-    Basic_Kind_Rawptr,
-
-    Basic_Kind_I8X16,
-    Basic_Kind_I16X8,
-    Basic_Kind_I32X4,
-    Basic_Kind_I64X2,
-    Basic_Kind_F32X4,
-    Basic_Kind_F64X2,
-    Basic_Kind_V128,
-
-    Basic_Kind_Type_Index,
-
-    Basic_Kind_Count,
-};
-
-enum BasicFlag {
-    Basic_Flag_Boolean          = BH_BIT(0),
-    Basic_Flag_Integer          = BH_BIT(1),
-    Basic_Flag_Unsigned         = BH_BIT(2),
-    Basic_Flag_Float            = BH_BIT(3),
-    Basic_Flag_Pointer          = BH_BIT(4),
-
-    Basic_Flag_SIMD             = BH_BIT(5),
-
-    Basic_Flag_Type_Index       = BH_BIT(6),
-
-    Basic_Flag_Numeric          = Basic_Flag_Integer | Basic_Flag_Float,
-    Basic_Flag_Ordered          = Basic_Flag_Integer | Basic_Flag_Float | Basic_Flag_Pointer,
-    Basic_Flag_Equality         = Basic_Flag_Ordered | Basic_Flag_Type_Index | Basic_Flag_Boolean,
-    Basic_Flag_Constant_Type    = Basic_Flag_Boolean | Basic_Flag_Numeric | Basic_Flag_Pointer,
-    Basic_Flag_Numeric_Ordered  = Basic_Flag_Numeric | Basic_Flag_Ordered,
-};
-
-typedef struct TypeBasic {
-    enum BasicKind kind;
-    u32 flags;
-    u32 size, alignment; // NOTE: In bytes
-    const char* name;
-} TypeBasic;
-
-// NOTE: Forward declaration for some of the types below
-typedef struct Type Type;
-
-typedef struct StructMember {
-    u32 offset, idx;
-    Type *type;
-
-    // NOTE: Since the name is stored here, it may not be necessary to,
-    // store a hash table of struct members. There realistically will not
-    // be many struct members, and iterating through an array would be
-    // easier and less costly.                  - brendanfh 2020/09/17
-    char *name;
-    struct OnyxToken* token;
-
-    struct AstTyped** initial_value;
-    i32 use_through_pointer_index;
-    b32 included_through_use : 1;
-    b32 used : 1;
-
-    bh_arr(struct AstTyped *) meta_tags;
-} StructMember;
-
-typedef struct TypeWithOffset TypeWithOffset;
-struct TypeWithOffset {
-    Type* type;
-    u32   offset;
-};
-
-typedef enum StructProcessingStatus {
-    SPS_Start,
-    SPS_Members_Done,
-    SPS_Uses_Done,
-} StructProcessingStatus;
-
-#define TYPE_KINDS \
-    TYPE_KIND(Basic, TypeBasic)                                   \
-    TYPE_KIND(Pointer, struct { TypeBasic base; Type *elem; })    \
-    TYPE_KIND(Function, struct {                                  \
-        Type *return_type;                                        \
-        u16 param_count;                                          \
-        u16 needed_param_count;                                   \
-        i16 vararg_arg_pos;                                       \
-        Type* params[];                                           \
-    })                                                            \
-    TYPE_KIND(Struct, struct {                                    \
-        char* name;                                               \
-        u32 size;                                                 \
-        u16 alignment, mem_count;                                 \
-        Table(StructMember *) members;                            \
-        bh_arr(StructMember *) memarr;                            \
-        bh_arr(struct AstPolySolution) poly_sln;                  \
-        bh_arr(TypeWithOffset) linear_members;                    \
-        struct AstType *constructed_from;                         \
-        bh_arr(struct AstTyped *) meta_tags;                      \
-        StructProcessingStatus status;                            \
-    })                                                            \
-    TYPE_KIND(PolyStruct, struct {                                \
-        char* name;                                               \
-        bh_arr(struct AstTyped *) meta_tags;                      \
-    })                                                            \
-    TYPE_KIND(Compound, struct {                                  \
-        u32 count;                                                \
-        u32 size;                                                 \
-        bh_arr(TypeWithOffset) linear_members;                    \
-        Type* types[];                                            \
-    })                                                            \
-    TYPE_KIND(Array, struct { Type* elem; u32 size; u32 count; }) \
-    TYPE_KIND(Slice, struct { Type *elem; })                      \
-    TYPE_KIND(DynArray, struct { Type *elem; })                   \
-    TYPE_KIND(VarArgs, struct { Type *elem; })                    \
-    TYPE_KIND(Enum, struct {                                      \
-        char* name;                                               \
-        Type* backing;                                            \
-        b32   is_flags;                                           \
-    })                                                            \
-    TYPE_KIND(Distinct, struct {                                  \
-        char* name;                                               \
-        Type* base_type;                                          \
-    })
-
-
-typedef enum TypeKind {
-    Type_Kind_Invalid,
-
-#define TYPE_KIND(k, ...) Type_Kind_##k,
-    TYPE_KINDS
-#undef TYPE_KIND
-
-    Type_Kind_Count,
-} TypeKind;
-
-#define TYPE_KIND(k, ...) typedef __VA_ARGS__ Type ## k;
-    TYPE_KINDS
-#undef TYPE_KIND
-
-enum TypeFlag {
-    Type_Flag_Default
-};
-
-struct Type {
-    TypeKind kind;
-
-    u32 id;
-    u32 flags;
-
-    // NOTE(Brendan Hansen): The abstract syntax tree node used to create
-    // the type. Primarily used to look up symbols in scopes that are embedded
-    // in the type.
-    struct AstType* ast_type;
-
-    union {
-#define TYPE_KIND(k, ...) Type##k k;
-        TYPE_KINDS
-#undef TYPE_KIND
-    };
-};
-
-extern bh_imap type_map;
-
-extern Type basic_types[];
-
-struct AstType;
-struct AstFunction;
-struct AstCompound;
-struct AstStructLiteral;
-
-void types_init();
-void types_dump_type_info();
-
-b32 types_are_compatible(Type* t1, Type* t2);
-u32 type_size_of(Type* type);
-u32 type_alignment_of(Type* type);
-Type* type_build_from_ast(bh_allocator alloc, struct AstType* type_node);
-Type* type_build_implicit_type_of_struct_literal(bh_allocator alloc, struct AstStructLiteral* lit);
-
-Type* type_build_function_type(bh_allocator alloc, struct AstFunction* func);
-Type* type_build_compound_type(bh_allocator alloc, struct AstCompound* compound);
-
-Type* type_make_pointer(bh_allocator alloc, Type* to);
-Type* type_make_array(bh_allocator alloc, Type* to, u32 count);
-Type* type_make_slice(bh_allocator alloc, Type* of);
-Type* type_make_dynarray(bh_allocator alloc, Type* of);
-Type* type_make_varargs(bh_allocator alloc, Type* of);
-
-void build_linear_types_with_offset(Type* type, bh_arr(TypeWithOffset)* pdest, u32 offset);
-b32  type_struct_member_apply_use(bh_allocator alloc, Type *s_type, StructMember *smem);
-
-const char* type_get_unique_name(Type* type);
-const char* type_get_name(Type* type);
-u32 type_get_alignment_log2(Type* type);
-Type* type_get_contained_type(Type* type);
-
-b32 type_lookup_member(Type* type, char* member, StructMember* smem);
-b32 type_lookup_member_by_idx(Type* type, i32 idx, StructMember* smem);
-
-i32 type_linear_member_count(Type* type);
-b32 type_linear_member_lookup(Type* type, i32 idx, TypeWithOffset* two);
-i32 type_get_idx_of_linear_member_with_offset(Type* type, u32 offset);
-
-b32 type_struct_is_simple(Type* type);
-
-b32 type_is_pointer(Type* type);
-b32 type_is_rawptr(Type* type);
-b32 type_is_array(Type* tyoe);
-b32 type_is_struct(Type* type);
-b32 type_is_bool(Type* type);
-b32 type_is_small_integer(Type* type);
-b32 type_is_integer(Type* type);
-b32 type_is_numeric(Type* type);
-b32 type_is_compound(Type* type);
-b32 type_is_simd(Type* type);
-b32 type_results_in_void(Type* type);
-b32 type_is_array_accessible(Type* type);
-b32 type_is_structlike(Type* type);
-b32 type_is_structlike_strict(Type* type);
-u32 type_structlike_mem_count(Type* type);
-u32 type_structlike_is_simple(Type* type);
-b32 type_is_sl_constructable(Type* type);
-b32 type_struct_constructed_from_poly_struct(Type* struct_type, struct AstType* from);
-
-#endif // #ifndef ONYX_TYPES
diff --git a/include/utils.h b/include/utils.h
deleted file mode 100644 (file)
index 2b0083c..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-#include "bh.h"
-
-#include "astnodes.h"
-
-extern bh_scratch global_scratch;
-extern bh_allocator global_scratch_allocator;
-
-extern bh_managed_heap global_heap;
-extern bh_allocator global_heap_allocator;
-
-const char* onyx_ast_node_kind_string(AstKind kind);
-
-Package* package_lookup(char* package_name);
-Package* package_lookup_or_create(char* package_name, Scope* parent_scope, bh_allocator alloc, OnyxFilePos pos);
-void package_track_use_package(Package* package, Entity* entity);
-void package_reinsert_use_packages(Package* package);
-
-Scope* scope_create(bh_allocator a, Scope* parent, OnyxFilePos created_at);
-void scope_include(Scope* target, Scope* source, OnyxFilePos pos);
-b32 symbol_introduce(Scope* scope, OnyxToken* tkn, AstNode* symbol);
-b32 symbol_raw_introduce(Scope* scope, char* tkn, OnyxFilePos pos, AstNode* symbol);
-void symbol_builtin_introduce(Scope* scope, char* sym, AstNode *node);
-void symbol_subpackage_introduce(Scope* scope, char* sym, AstPackage *node);
-AstNode* symbol_raw_resolve(Scope* start_scope, char* sym);
-AstNode* symbol_resolve(Scope* start_scope, OnyxToken* tkn);
-AstNode* try_symbol_raw_resolve_from_node(AstNode* node, char* symbol);
-AstNode* try_symbol_resolve_from_node(AstNode* node, OnyxToken* token);
-AstNode* try_symbol_raw_resolve_from_type(Type *type, char* symbol);
-Scope *get_scope_from_node(AstNode *node);
-Scope *get_scope_from_node_or_create(AstNode *node);
-
-void build_all_overload_options(bh_arr(OverloadOption) overloads, bh_imap* all_overloads);
-
-u32 char_to_base16_value(char x);
-
-// Returns the length after processing the string.
-i32 string_process_escape_seqs(char* dest, char* src, i32 len);
-
-u32 levenshtein_distance(const char *str1, const char *str2);
-char *find_closest_symbol_in_scope_and_parents(Scope *scope, char *sym);
-char *find_closest_symbol_in_node(AstNode *node, char *sym);
-
-extern AstTyped node_that_signals_a_yield;
-extern AstTyped node_that_signals_failure;
diff --git a/include/wasm_emit.h b/include/wasm_emit.h
deleted file mode 100644 (file)
index 3a79e54..0000000
+++ /dev/null
@@ -1,864 +0,0 @@
-#ifndef ONYXWASM_H
-#define ONYXWASM_H
-
-#include "bh.h"
-
-#include "astnodes.h"
-#include "errors.h"
-
-typedef u8 WasmType;
-
-typedef struct WasmFuncType {
-    // NOTE: For now, WASM only allows for 1 return value.
-    // This may be lifted in the future.
-    i32 param_count;
-    WasmType return_type;
-    WasmType param_types[];
-} WasmFuncType;
-
-#define SIMD_INSTR_MASK   0x10000
-#define EXT_INSTR_MASK    0x20000
-#define ATOMIC_INSTR_MASK 0x40000
-
-typedef enum WasmInstructionType {
-    WI_UNREACHABLE                   = 0x00,
-    WI_NOP                           = 0x01,
-
-    // NOTE: Control flow
-    WI_BLOCK_START                   = 0x02,
-    WI_BLOCK_END                     = 0x0B, // NOTE: These ends are not unique
-    WI_LOOP_START                    = 0x03,
-    WI_LOOP_END                      = 0x0B,
-    WI_IF_START                      = 0x04,
-    WI_ELSE                          = 0x05,
-    WI_IF_END                        = 0x0B,
-    WI_JUMP                          = 0x0C,
-    WI_COND_JUMP                     = 0x0D,
-    WI_JUMP_TABLE                    = 0x0E,
-    WI_RETURN                        = 0x0F,
-    WI_CALL                          = 0x10,
-    WI_CALL_INDIRECT                 = 0x11,
-
-    // NOTE: Parametric instructions
-    WI_DROP                          = 0x1A,
-    WI_SELECT                        = 0x1B,
-
-    // NOTE: Variable instructions
-    WI_LOCAL_GET                     = 0x20,
-    WI_LOCAL_SET                     = 0x21,
-    WI_LOCAL_TEE                     = 0x22,
-    WI_GLOBAL_GET                    = 0x23,
-    WI_GLOBAL_SET                    = 0x24,
-
-    // NOTE: Memory instructions
-    WI_I32_LOAD                      = 0x28,
-    WI_I64_LOAD                      = 0x29,
-    WI_F32_LOAD                      = 0x2A,
-    WI_F64_LOAD                      = 0x2B,
-    WI_I32_LOAD_8_S                  = 0x2C,
-    WI_I32_LOAD_8_U                  = 0x2D,
-    WI_I32_LOAD_16_S                 = 0x2E,
-    WI_I32_LOAD_16_U                 = 0x2F,
-    WI_I64_LOAD_8_S                  = 0x30,
-    WI_I64_LOAD_8_U                  = 0x31,
-    WI_I64_LOAD_16_S                 = 0x32,
-    WI_I64_LOAD_16_U                 = 0x33,
-    WI_I64_LOAD_32_S                 = 0x34,
-    WI_I64_LOAD_32_U                 = 0x35,
-    WI_I32_STORE                     = 0x36,
-    WI_I64_STORE                     = 0x37,
-    WI_F32_STORE                     = 0x38,
-    WI_F64_STORE                     = 0x39,
-    WI_I32_STORE_8                   = 0x3A,
-    WI_I32_STORE_16                  = 0x3B,
-    WI_I64_STORE_8                   = 0x3C,
-    WI_I64_STORE_16                  = 0x3D,
-    WI_I64_STORE_32                  = 0x3E,
-    WI_MEMORY_SIZE                   = 0x3F,
-    WI_MEMORY_GROW                   = 0x40,
-
-    // NOTE: Numeric Instructions
-    WI_I32_CONST                     = 0x41,
-    WI_I64_CONST                     = 0x42,
-    WI_F32_CONST                     = 0x43,
-    WI_F64_CONST                     = 0x44,
-
-    WI_I32_EQZ                       = 0x45,
-    WI_I32_EQ                        = 0x46,
-    WI_I32_NE                        = 0x47,
-    WI_I32_LT_S                      = 0x48,
-    WI_I32_LT_U                      = 0x49,
-    WI_I32_GT_S                      = 0x4a,
-    WI_I32_GT_U                      = 0x4b,
-    WI_I32_LE_S                      = 0x4c,
-    WI_I32_LE_U                      = 0x4d,
-    WI_I32_GE_S                      = 0x4e,
-    WI_I32_GE_U                      = 0x4f,
-
-    WI_I64_EQZ                       = 0x50,
-    WI_I64_EQ                        = 0x51,
-    WI_I64_NE                        = 0x52,
-    WI_I64_LT_S                      = 0x53,
-    WI_I64_LT_U                      = 0x54,
-    WI_I64_GT_S                      = 0x55,
-    WI_I64_GT_U                      = 0x56,
-    WI_I64_LE_S                      = 0x57,
-    WI_I64_LE_U                      = 0x58,
-    WI_I64_GE_S                      = 0x59,
-    WI_I64_GE_U                      = 0x5a,
-
-    WI_F32_EQ                        = 0x5b,
-    WI_F32_NE                        = 0x5c,
-    WI_F32_LT                        = 0x5d,
-    WI_F32_GT                        = 0x5e,
-    WI_F32_LE                        = 0x5f,
-    WI_F32_GE                        = 0x60,
-
-    WI_F64_EQ                        = 0x61,
-    WI_F64_NE                        = 0x62,
-    WI_F64_LT                        = 0x63,
-    WI_F64_GT                        = 0x64,
-    WI_F64_LE                        = 0x65,
-    WI_F64_GE                        = 0x66,
-
-    WI_I32_CLZ                       = 0x67,
-    WI_I32_CTZ                       = 0x68,
-    WI_I32_POPCNT                    = 0x69,
-    WI_I32_ADD                       = 0x6a,
-    WI_I32_SUB                       = 0x6b,
-    WI_I32_MUL                       = 0x6c,
-    WI_I32_DIV_S                     = 0x6d,
-    WI_I32_DIV_U                     = 0x6e,
-    WI_I32_REM_S                     = 0x6f,
-    WI_I32_REM_U                     = 0x70,
-    WI_I32_AND                       = 0x71,
-    WI_I32_OR                        = 0x72,
-    WI_I32_XOR                       = 0x73,
-    WI_I32_SHL                       = 0x74,
-    WI_I32_SHR_S                     = 0x75,
-    WI_I32_SHR_U                     = 0x76,
-    WI_I32_ROTL                      = 0x77,
-    WI_I32_ROTR                      = 0x78,
-
-    WI_I64_CLZ                       = 0x79,
-    WI_I64_CTZ                       = 0x7a,
-    WI_I64_POPCNT                    = 0x7b,
-    WI_I64_ADD                       = 0x7c,
-    WI_I64_SUB                       = 0x7d,
-    WI_I64_MUL                       = 0x7e,
-    WI_I64_DIV_S                     = 0x7f,
-    WI_I64_DIV_U                     = 0x80,
-    WI_I64_REM_S                     = 0x81,
-    WI_I64_REM_U                     = 0x82,
-    WI_I64_AND                       = 0x83,
-    WI_I64_OR                        = 0x84,
-    WI_I64_XOR                       = 0x85,
-    WI_I64_SHL                       = 0x86,
-    WI_I64_SHR_S                     = 0x87,
-    WI_I64_SHR_U                     = 0x88,
-    WI_I64_ROTL                      = 0x89,
-    WI_I64_ROTR                      = 0x8a,
-
-    WI_F32_ABS                       = 0x8b,
-    WI_F32_NEG                       = 0x8c,
-    WI_F32_CEIL                      = 0x8d,
-    WI_F32_FLOOR                     = 0x8e,
-    WI_F32_TRUNC                     = 0x8f,
-    WI_F32_NEAREST                   = 0x90,
-    WI_F32_SQRT                      = 0x91,
-    WI_F32_ADD                       = 0x92,
-    WI_F32_SUB                       = 0x93,
-    WI_F32_MUL                       = 0x94,
-    WI_F32_DIV                       = 0x95,
-    WI_F32_MIN                       = 0x96,
-    WI_F32_MAX                       = 0x97,
-    WI_F32_COPYSIGN                  = 0x98,
-
-    WI_F64_ABS                       = 0x99,
-    WI_F64_NEG                       = 0x9a,
-    WI_F64_CEIL                      = 0x9b,
-    WI_F64_FLOOR                     = 0x9c,
-    WI_F64_TRUNC                     = 0x9d,
-    WI_F64_NEAREST                   = 0x9e,
-    WI_F64_SQRT                      = 0x9f,
-    WI_F64_ADD                       = 0xA0,
-    WI_F64_SUB                       = 0xA1,
-    WI_F64_MUL                       = 0xA2,
-    WI_F64_DIV                       = 0xA3,
-    WI_F64_MIN                       = 0xA4,
-    WI_F64_MAX                       = 0xA5,
-    WI_F64_COPYSIGN                  = 0xA6,
-
-    WI_I32_FROM_I64                  = 0xA7,
-    WI_I32_FROM_F32_S                = 0xA8,
-    WI_I32_FROM_F32_U                = 0xA9,
-    WI_I32_FROM_F64_S                = 0xAA,
-    WI_I32_FROM_F64_U                = 0xAB,
-
-    WI_I64_FROM_I32_S                = 0xAC,
-    WI_I64_FROM_I32_U                = 0xAD,
-    WI_I64_FROM_F32_S                = 0xAE,
-    WI_I64_FROM_F32_U                = 0xAF,
-    WI_I64_FROM_F64_S                = 0xB0,
-    WI_I64_FROM_F64_U                = 0xB1,
-
-    WI_F32_FROM_I32_S                = 0xB2,
-    WI_F32_FROM_I32_U                = 0xB3,
-    WI_F32_FROM_I64_S                = 0xB4,
-    WI_F32_FROM_I64_U                = 0xB5,
-    WI_F32_FROM_F64                  = 0xB6,
-
-    WI_F64_FROM_I32_S                = 0xB7,
-    WI_F64_FROM_I32_U                = 0xB8,
-    WI_F64_FROM_I64_S                = 0xB9,
-    WI_F64_FROM_I64_U                = 0xBA,
-    WI_F64_FROM_F32                  = 0xBB,
-
-    WI_I32_REINTERPRET_F32           = 0xBC,
-    WI_I64_REINTERPRET_F64           = 0xBD,
-    WI_F32_REINTERPRET_I32           = 0xBE,
-    WI_F64_REINTERPRET_I64           = 0xBF,
-
-    WI_I32_EXTEND_8_S                = 0xC0,
-    WI_I32_EXTEND_16_S               = 0xC1,
-    WI_I64_EXTEND_8_S                = 0xC2,
-    WI_I64_EXTEND_16_S               = 0xC3,
-    WI_I64_EXTEND_32_S               = 0xC4,
-
-    // Pointer stuff; this will make it easier to switch to 64-bit
-    // pointers, whenever WebAssembly standarizes that.
-    WI_PTR_CONST                     = WI_I32_CONST,
-    WI_PTR_LOAD                      = WI_I32_LOAD,
-    WI_PTR_STORE                     = WI_I32_STORE,
-    WI_PTR_ADD                       = WI_I32_ADD,
-    WI_PTR_SUB                       = WI_I32_SUB,
-    WI_PTR_MUL                       = WI_I32_MUL,
-    WI_PTR_GE                        = WI_I32_GE_U,
-    WI_PTR_GT                        = WI_I32_GT_U,
-    WI_PTR_EQ                        = WI_I32_EQ,
-
-    WI_V128_LOAD                     = SIMD_INSTR_MASK | 0,
-    WI_V128_STORE                    = SIMD_INSTR_MASK | 11,
-
-    WI_V128_CONST                    = SIMD_INSTR_MASK | 12,
-
-    WI_I8X16_SHUFFLE                 = SIMD_INSTR_MASK | 13,
-
-    WI_I8X16_EXTRACT_LANE_S          = SIMD_INSTR_MASK | 21,
-    WI_I8X16_EXTRACT_LANE_U          = SIMD_INSTR_MASK | 22,
-    WI_I8X16_REPLACE_LANE            = SIMD_INSTR_MASK | 23,
-    WI_I16X8_EXTRACT_LANE_S          = SIMD_INSTR_MASK | 24,
-    WI_I16X8_EXTRACT_LANE_U          = SIMD_INSTR_MASK | 25,
-    WI_I16X8_REPLACE_LANE            = SIMD_INSTR_MASK | 26,
-    WI_I32X4_EXTRACT_LANE            = SIMD_INSTR_MASK | 27,
-    WI_I32X4_REPLACE_LANE            = SIMD_INSTR_MASK | 28,
-    WI_I64X2_EXTRACT_LANE            = SIMD_INSTR_MASK | 29,
-    WI_I64X2_REPLACE_LANE            = SIMD_INSTR_MASK | 30,
-    WI_F32X4_EXTRACT_LANE            = SIMD_INSTR_MASK | 31,
-    WI_F32X4_REPLACE_LANE            = SIMD_INSTR_MASK | 32,
-    WI_F64X2_EXTRACT_LANE            = SIMD_INSTR_MASK | 33,
-    WI_F64X2_REPLACE_LANE            = SIMD_INSTR_MASK | 34,
-
-    WI_I8X16_SWIZZLE                 = SIMD_INSTR_MASK | 14,
-    WI_I8X16_SPLAT                   = SIMD_INSTR_MASK | 15,
-    WI_I16X8_SPLAT                   = SIMD_INSTR_MASK | 16,
-    WI_I32X4_SPLAT                   = SIMD_INSTR_MASK | 17,
-    WI_I64X2_SPLAT                   = SIMD_INSTR_MASK | 18,
-    WI_F32X4_SPLAT                   = SIMD_INSTR_MASK | 19,
-    WI_F64X2_SPLAT                   = SIMD_INSTR_MASK | 20,
-
-    WI_I8X16_EQ                      = SIMD_INSTR_MASK | 35,
-    WI_I8X16_NEQ                     = SIMD_INSTR_MASK | 36,
-    WI_I8X16_LT_S                    = SIMD_INSTR_MASK | 37,
-    WI_I8X16_LT_U                    = SIMD_INSTR_MASK | 38,
-    WI_I8X16_GT_S                    = SIMD_INSTR_MASK | 39,
-    WI_I8X16_GT_U                    = SIMD_INSTR_MASK | 40,
-    WI_I8X16_LE_S                    = SIMD_INSTR_MASK | 41,
-    WI_I8X16_LE_U                    = SIMD_INSTR_MASK | 42,
-    WI_I8X16_GE_S                    = SIMD_INSTR_MASK | 43,
-    WI_I8X16_GE_U                    = SIMD_INSTR_MASK | 44,
-
-    WI_I16X8_EQ                      = SIMD_INSTR_MASK | 45,
-    WI_I16X8_NEQ                     = SIMD_INSTR_MASK | 46,
-    WI_I16X8_LT_S                    = SIMD_INSTR_MASK | 47,
-    WI_I16X8_LT_U                    = SIMD_INSTR_MASK | 48,
-    WI_I16X8_GT_S                    = SIMD_INSTR_MASK | 49,
-    WI_I16X8_GT_U                    = SIMD_INSTR_MASK | 50,
-    WI_I16X8_LE_S                    = SIMD_INSTR_MASK | 51,
-    WI_I16X8_LE_U                    = SIMD_INSTR_MASK | 52,
-    WI_I16X8_GE_S                    = SIMD_INSTR_MASK | 53,
-    WI_I16X8_GE_U                    = SIMD_INSTR_MASK | 54,
-
-    WI_I32X4_EQ                      = SIMD_INSTR_MASK | 55,
-    WI_I32X4_NEQ                     = SIMD_INSTR_MASK | 56,
-    WI_I32X4_LT_S                    = SIMD_INSTR_MASK | 57,
-    WI_I32X4_LT_U                    = SIMD_INSTR_MASK | 58,
-    WI_I32X4_GT_S                    = SIMD_INSTR_MASK | 59,
-    WI_I32X4_GT_U                    = SIMD_INSTR_MASK | 60,
-    WI_I32X4_LE_S                    = SIMD_INSTR_MASK | 61,
-    WI_I32X4_LE_U                    = SIMD_INSTR_MASK | 62,
-    WI_I32X4_GE_S                    = SIMD_INSTR_MASK | 63,
-    WI_I32X4_GE_U                    = SIMD_INSTR_MASK | 64,
-
-    WI_F32X4_EQ                      = SIMD_INSTR_MASK | 65,
-    WI_F32X4_NEQ                     = SIMD_INSTR_MASK | 66,
-    WI_F32X4_LT                      = SIMD_INSTR_MASK | 67,
-    WI_F32X4_GT                      = SIMD_INSTR_MASK | 68,
-    WI_F32X4_LE                      = SIMD_INSTR_MASK | 69,
-    WI_F32X4_GE                      = SIMD_INSTR_MASK | 70,
-
-    WI_F64X2_EQ                      = SIMD_INSTR_MASK | 71,
-    WI_F64X2_NEQ                     = SIMD_INSTR_MASK | 72,
-    WI_F64X2_LT                      = SIMD_INSTR_MASK | 73,
-    WI_F64X2_GT                      = SIMD_INSTR_MASK | 74,
-    WI_F64X2_LE                      = SIMD_INSTR_MASK | 75,
-    WI_F64X2_GE                      = SIMD_INSTR_MASK | 76,
-
-    WI_V128_NOT                      = SIMD_INSTR_MASK | 77,
-    WI_V128_AND                      = SIMD_INSTR_MASK | 78,
-    WI_V128_ANDNOT                   = SIMD_INSTR_MASK | 79,
-    WI_V128_OR                       = SIMD_INSTR_MASK | 80,
-    WI_V128_XOR                      = SIMD_INSTR_MASK | 81,
-    WI_V128_BITSELECT                = SIMD_INSTR_MASK | 82,
-
-    WI_I8X16_ABS                     = SIMD_INSTR_MASK | 96,
-    WI_I8X16_NEG                     = SIMD_INSTR_MASK | 97,
-    WI_I8X16_ANY_TRUE                = SIMD_INSTR_MASK | 98,
-    WI_I8X16_ALL_TRUE                = SIMD_INSTR_MASK | 99,
-    WI_I8X16_BITMASK                 = SIMD_INSTR_MASK | 100,
-    WI_I8X16_NARROW_I16X8_S          = SIMD_INSTR_MASK | 101,
-    WI_I8X16_NARROW_I16X8_U          = SIMD_INSTR_MASK | 102,
-    WI_I8X16_SHL                     = SIMD_INSTR_MASK | 107,
-    WI_I8X16_SHR_S                   = SIMD_INSTR_MASK | 108,
-    WI_I8X16_SHR_U                   = SIMD_INSTR_MASK | 109,
-    WI_I8X16_ADD                     = SIMD_INSTR_MASK | 110,
-    WI_I8X16_ADD_SAT_S               = SIMD_INSTR_MASK | 111,
-    WI_I8X16_ADD_SAT_U               = SIMD_INSTR_MASK | 112,
-    WI_I8X16_SUB                     = SIMD_INSTR_MASK | 113,
-    WI_I8X16_SUB_SAT_S               = SIMD_INSTR_MASK | 114,
-    WI_I8X16_SUB_SAT_U               = SIMD_INSTR_MASK | 115,
-    WI_I8X16_MIN_S                   = SIMD_INSTR_MASK | 118,
-    WI_I8X16_MIN_U                   = SIMD_INSTR_MASK | 119,
-    WI_I8X16_MAX_S                   = SIMD_INSTR_MASK | 120,
-    WI_I8X16_MAX_U                   = SIMD_INSTR_MASK | 121,
-    WI_I8X16_AVGR_U                  = SIMD_INSTR_MASK | 123,
-
-    WI_I16X8_ABS                     = SIMD_INSTR_MASK | 128,
-    WI_I16X8_NEG                     = SIMD_INSTR_MASK | 129,
-    WI_I16X8_ANY_TRUE                = SIMD_INSTR_MASK | 130,
-    WI_I16X8_ALL_TRUE                = SIMD_INSTR_MASK | 131,
-    WI_I16X8_BITMASK                 = SIMD_INSTR_MASK | 132,
-    WI_I16X8_NARROW_I32X4_S          = SIMD_INSTR_MASK | 133,
-    WI_I16X8_NARROW_I32X4_U          = SIMD_INSTR_MASK | 134,
-    WI_I16X8_WIDEN_LOW_I8X16_S       = SIMD_INSTR_MASK | 135,
-    WI_I16X8_WIDEN_HIGH_I8X16_S      = SIMD_INSTR_MASK | 136,
-    WI_I16X8_WIDEN_LOW_I8X16_U       = SIMD_INSTR_MASK | 137,
-    WI_I16X8_WIDEN_HIGH_I8X16_U      = SIMD_INSTR_MASK | 138,
-    WI_I16X8_SHL                     = SIMD_INSTR_MASK | 139,
-    WI_I16X8_SHR_S                   = SIMD_INSTR_MASK | 140,
-    WI_I16X8_SHR_U                   = SIMD_INSTR_MASK | 141,
-    WI_I16X8_ADD                     = SIMD_INSTR_MASK | 142,
-    WI_I16X8_ADD_SAT_S               = SIMD_INSTR_MASK | 143,
-    WI_I16X8_ADD_SAT_U               = SIMD_INSTR_MASK | 144,
-    WI_I16X8_SUB                     = SIMD_INSTR_MASK | 145,
-    WI_I16X8_SUB_SAT_S               = SIMD_INSTR_MASK | 146,
-    WI_I16X8_SUB_SAT_U               = SIMD_INSTR_MASK | 147,
-    WI_I16X8_MUL                     = SIMD_INSTR_MASK | 149,
-    WI_I16X8_MIN_S                   = SIMD_INSTR_MASK | 150,
-    WI_I16X8_MIN_U                   = SIMD_INSTR_MASK | 151,
-    WI_I16X8_MAX_S                   = SIMD_INSTR_MASK | 152,
-    WI_I16X8_MAX_U                   = SIMD_INSTR_MASK | 153,
-    WI_I16X8_AVGR_U                  = SIMD_INSTR_MASK | 155,
-
-    WI_I32X4_ABS                     = SIMD_INSTR_MASK | 160,
-    WI_I32X4_NEG                     = SIMD_INSTR_MASK | 161,
-    WI_I32X4_ANY_TRUE                = SIMD_INSTR_MASK | 162,
-    WI_I32X4_ALL_TRUE                = SIMD_INSTR_MASK | 163,
-    WI_I32X4_BITMASK                 = SIMD_INSTR_MASK | 164,
-    WI_I32X4_WIDEN_LOW_I16X8_S       = SIMD_INSTR_MASK | 167,
-    WI_I32X4_WIDEN_HIGH_I16X8_S      = SIMD_INSTR_MASK | 168,
-    WI_I32X4_WIDEN_LOW_I16X8_U       = SIMD_INSTR_MASK | 169,
-    WI_I32X4_WIDEN_HIGH_I16X8_U      = SIMD_INSTR_MASK | 170,
-    WI_I32X4_SHL                     = SIMD_INSTR_MASK | 171,
-    WI_I32X4_SHR_S                   = SIMD_INSTR_MASK | 172,
-    WI_I32X4_SHR_U                   = SIMD_INSTR_MASK | 173,
-    WI_I32X4_ADD                     = SIMD_INSTR_MASK | 174,
-    WI_I32X4_SUB                     = SIMD_INSTR_MASK | 177,
-    WI_I32X4_MUL                     = SIMD_INSTR_MASK | 181,
-    WI_I32X4_MIN_S                   = SIMD_INSTR_MASK | 182,
-    WI_I32X4_MIN_U                   = SIMD_INSTR_MASK | 183,
-    WI_I32X4_MAX_S                   = SIMD_INSTR_MASK | 184,
-    WI_I32X4_MAX_U                   = SIMD_INSTR_MASK | 185,
-
-    WI_I64X2_NEG                     = SIMD_INSTR_MASK | 193,
-    WI_I64X2_SHL                     = SIMD_INSTR_MASK | 203,
-    WI_I64X2_SHR_S                   = SIMD_INSTR_MASK | 204,
-    WI_I64X2_SHR_U                   = SIMD_INSTR_MASK | 205,
-    WI_I64X2_ADD                     = SIMD_INSTR_MASK | 206,
-    WI_I64X2_SUB                     = SIMD_INSTR_MASK | 209,
-    WI_I64X2_MUL                     = SIMD_INSTR_MASK | 213,
-
-    WI_F32X4_ABS                     = SIMD_INSTR_MASK | 224,
-    WI_F32X4_NEG                     = SIMD_INSTR_MASK | 225,
-    WI_F32X4_SQRT                    = SIMD_INSTR_MASK | 227,
-    WI_F32X4_ADD                     = SIMD_INSTR_MASK | 228,
-    WI_F32X4_SUB                     = SIMD_INSTR_MASK | 229,
-    WI_F32X4_MUL                     = SIMD_INSTR_MASK | 230,
-    WI_F32X4_DIV                     = SIMD_INSTR_MASK | 231,
-    WI_F32X4_MIN                     = SIMD_INSTR_MASK | 232,
-    WI_F32X4_MAX                     = SIMD_INSTR_MASK | 233,
-
-    WI_F64X2_ABS                     = SIMD_INSTR_MASK | 236,
-    WI_F64X2_NEG                     = SIMD_INSTR_MASK | 237,
-    WI_F64X2_SQRT                    = SIMD_INSTR_MASK | 239,
-    WI_F64X2_ADD                     = SIMD_INSTR_MASK | 240,
-    WI_F64X2_SUB                     = SIMD_INSTR_MASK | 241,
-    WI_F64X2_MUL                     = SIMD_INSTR_MASK | 242,
-    WI_F64X2_DIV                     = SIMD_INSTR_MASK | 243,
-    WI_F64X2_MIN                     = SIMD_INSTR_MASK | 244,
-    WI_F64X2_MAX                     = SIMD_INSTR_MASK | 245,
-
-    WI_I32X4_TRUNC_SAT_F32X4_S       = SIMD_INSTR_MASK | 248,
-    WI_I32X4_TRUNC_SAT_F32X4_U       = SIMD_INSTR_MASK | 249,
-    WI_F32X4_CONVERT_I32X4_S         = SIMD_INSTR_MASK | 250,
-    WI_F32X4_CONVERT_I32X4_U         = SIMD_INSTR_MASK | 251,
-    
-    
-    WI_MEMORY_INIT                   = EXT_INSTR_MASK | 0x08,
-    WI_MEMORY_COPY                   = EXT_INSTR_MASK | 0x0a,
-    WI_MEMORY_FILL                   = EXT_INSTR_MASK | 0x0b,
-
-    WI_ATOMIC_NOTIFY                 = ATOMIC_INSTR_MASK | 0x00,
-    WI_ATOMIC_WAIT32                 = ATOMIC_INSTR_MASK | 0x01,
-    WI_ATOMIC_WAIT64                 = ATOMIC_INSTR_MASK | 0x02,
-
-    WI_ATOMIC_FENCE                  = ATOMIC_INSTR_MASK | 0x03,
-
-    WI_ATOMIC_I32_LOAD               = ATOMIC_INSTR_MASK | 0x10,
-    WI_ATOMIC_I64_LOAD               = ATOMIC_INSTR_MASK | 0x11,
-    WI_ATOMIC_I32_LOAD8_U            = ATOMIC_INSTR_MASK | 0x12,
-    WI_ATOMIC_I32_LOAD16_U           = ATOMIC_INSTR_MASK | 0x13,
-    WI_ATOMIC_I64_LOAD8_U            = ATOMIC_INSTR_MASK | 0x14,
-    WI_ATOMIC_I64_LOAD16_U           = ATOMIC_INSTR_MASK | 0x15,
-    WI_ATOMIC_I64_LOAD32_U           = ATOMIC_INSTR_MASK | 0x16,
-
-    WI_ATOMIC_I32_STORE              = ATOMIC_INSTR_MASK | 0x17,
-    WI_ATOMIC_I64_STORE              = ATOMIC_INSTR_MASK | 0x18,
-    WI_ATOMIC_I32_STORE8             = ATOMIC_INSTR_MASK | 0x19,
-    WI_ATOMIC_I32_STORE16            = ATOMIC_INSTR_MASK | 0x1a,
-    WI_ATOMIC_I64_STORE8             = ATOMIC_INSTR_MASK | 0x1b,
-    WI_ATOMIC_I64_STORE16            = ATOMIC_INSTR_MASK | 0x1c,
-    WI_ATOMIC_I64_STORE32            = ATOMIC_INSTR_MASK | 0x1d,
-
-    WI_ATOMIC_I32_ADD                = ATOMIC_INSTR_MASK | 0x1e,
-    WI_ATOMIC_I64_ADD                = ATOMIC_INSTR_MASK | 0x1f,
-    WI_ATOMIC_I32_ADD8_U             = ATOMIC_INSTR_MASK | 0x20,
-    WI_ATOMIC_I32_ADD16_U            = ATOMIC_INSTR_MASK | 0x21,
-    WI_ATOMIC_I64_ADD8_U             = ATOMIC_INSTR_MASK | 0x22,
-    WI_ATOMIC_I64_ADD16_U            = ATOMIC_INSTR_MASK | 0x23,
-    WI_ATOMIC_I64_ADD32_U            = ATOMIC_INSTR_MASK | 0x24,
-
-    WI_ATOMIC_I32_SUB                = ATOMIC_INSTR_MASK | 0x25,
-    WI_ATOMIC_I64_SUB                = ATOMIC_INSTR_MASK | 0x26,
-    WI_ATOMIC_I32_SUB8_U             = ATOMIC_INSTR_MASK | 0x27,
-    WI_ATOMIC_I32_SUB16_U            = ATOMIC_INSTR_MASK | 0x28,
-    WI_ATOMIC_I64_SUB8_U             = ATOMIC_INSTR_MASK | 0x29,
-    WI_ATOMIC_I64_SUB16_U            = ATOMIC_INSTR_MASK | 0x2a,
-    WI_ATOMIC_I64_SUB32_U            = ATOMIC_INSTR_MASK | 0x2b,
-
-    WI_ATOMIC_I32_AND                = ATOMIC_INSTR_MASK | 0x2c,
-    WI_ATOMIC_I64_AND                = ATOMIC_INSTR_MASK | 0x2d,
-    WI_ATOMIC_I32_AND8_U             = ATOMIC_INSTR_MASK | 0x2e,
-    WI_ATOMIC_I32_AND16_U            = ATOMIC_INSTR_MASK | 0x2f,
-    WI_ATOMIC_I64_AND8_U             = ATOMIC_INSTR_MASK | 0x30,
-    WI_ATOMIC_I64_AND16_U            = ATOMIC_INSTR_MASK | 0x31,
-    WI_ATOMIC_I64_AND32_U            = ATOMIC_INSTR_MASK | 0x32,
-
-    WI_ATOMIC_I32_OR                 = ATOMIC_INSTR_MASK | 0x33,
-    WI_ATOMIC_I64_OR                 = ATOMIC_INSTR_MASK | 0x34,
-    WI_ATOMIC_I32_OR8_U              = ATOMIC_INSTR_MASK | 0x35,
-    WI_ATOMIC_I32_OR16_U             = ATOMIC_INSTR_MASK | 0x36,
-    WI_ATOMIC_I64_OR8_U              = ATOMIC_INSTR_MASK | 0x37,
-    WI_ATOMIC_I64_OR16_U             = ATOMIC_INSTR_MASK | 0x38,
-    WI_ATOMIC_I64_OR32_U             = ATOMIC_INSTR_MASK | 0x39,
-
-    WI_ATOMIC_I32_XOR                = ATOMIC_INSTR_MASK | 0x3a,
-    WI_ATOMIC_I64_XOR                = ATOMIC_INSTR_MASK | 0x3b,
-    WI_ATOMIC_I32_XOR8_U             = ATOMIC_INSTR_MASK | 0x3c,
-    WI_ATOMIC_I32_XOR16_U            = ATOMIC_INSTR_MASK | 0x3d,
-    WI_ATOMIC_I64_XOR8_U             = ATOMIC_INSTR_MASK | 0x3e,
-    WI_ATOMIC_I64_XOR16_U            = ATOMIC_INSTR_MASK | 0x3f,
-    WI_ATOMIC_I64_XOR32_U            = ATOMIC_INSTR_MASK | 0x40,
-
-    WI_ATOMIC_I32_XCHG               = ATOMIC_INSTR_MASK | 0x41,
-    WI_ATOMIC_I64_XCHG               = ATOMIC_INSTR_MASK | 0x42,
-    WI_ATOMIC_I32_XCHG8_U            = ATOMIC_INSTR_MASK | 0x43,
-    WI_ATOMIC_I32_XCHG16_U           = ATOMIC_INSTR_MASK | 0x44,
-    WI_ATOMIC_I64_XCHG8_U            = ATOMIC_INSTR_MASK | 0x45,
-    WI_ATOMIC_I64_XCHG16_U           = ATOMIC_INSTR_MASK | 0x46,
-    WI_ATOMIC_I64_XCHG32_U           = ATOMIC_INSTR_MASK | 0x47,
-
-    WI_ATOMIC_I32_CMPXCHG            = ATOMIC_INSTR_MASK | 0x48,
-    WI_ATOMIC_I64_CMPXCHG            = ATOMIC_INSTR_MASK | 0x49,
-    WI_ATOMIC_I32_CMPXCHG8_U         = ATOMIC_INSTR_MASK | 0x4a,
-    WI_ATOMIC_I32_CMPXCHG16_U        = ATOMIC_INSTR_MASK | 0x4b,
-    WI_ATOMIC_I64_CMPXCHG8_U         = ATOMIC_INSTR_MASK | 0x4c,
-    WI_ATOMIC_I64_CMPXCHG16_U        = ATOMIC_INSTR_MASK | 0x4d,
-    WI_ATOMIC_I64_CMPXCHG32_U        = ATOMIC_INSTR_MASK | 0x4e,
-} WasmInstructionType;
-
-typedef union {
-    struct {
-        i32 i1, i2;
-    };
-    i64 l;
-    float f;
-    double d;
-    ptr p;
-} WasmInstructionData;
-
-typedef struct WasmInstruction {
-    WasmInstructionType type;
-    WasmInstructionData data;
-} WasmInstruction;
-
-typedef struct BranchTable {
-    u32 count;
-    u32 default_case;
-    u32 cases[];
-} BranchTable;
-
-#define LOCAL_IS_WASM 0x8000000000000
-typedef struct LocalAllocator {
-    u32 param_count;
-
-    u32 allocated[5];
-    u32 freed[5];
-
-    i32 max_stack;
-    i32 curr_stack;
-} LocalAllocator;
-
-typedef struct WasmFunc {
-    i32 type_idx;
-    LocalAllocator locals;
-    bh_arr(WasmInstruction) code;
-    OnyxToken *location;
-} WasmFunc;
-
-typedef struct WasmGlobal {
-    WasmType type;
-    u32 mutable : 1;
-    bh_arr(WasmInstruction) initial_value;
-} WasmGlobal;
-
-typedef enum WasmForeignKind {
-    WASM_FOREIGN_FUNCTION   = 0x00,
-    WASM_FOREIGN_TABLE      = 0x01,
-    WASM_FOREIGN_MEMORY     = 0x02,
-    WASM_FOREIGN_GLOBAL     = 0x03,
-} WasmForeignKind;
-
-typedef struct WasmExport {
-    WasmForeignKind kind;
-    i32 idx;
-} WasmExport;
-
-typedef struct WasmImport {
-    WasmForeignKind kind;
-    union {
-        i32 idx;
-        struct {
-            i32 min, max;
-            b32 shared;
-        };
-    };
-    char *mod, *name;
-} WasmImport;
-
-typedef struct WasmDatum {
-    u32 id;
-    u32 offset_, alignment;
-    u32 length;
-    ptr data;
-} WasmDatum;
-
-typedef enum DatumPatchInfoKind {
-    Datum_Patch_Instruction,
-    Datum_Patch_Data,
-    Datum_Patch_Relative,
-} DatumPatchInfoKind;
-
-//
-// This represents a pointer that should be filled in
-// later when the corresponding data element is placed.
-//
-// There are three kinds of patches:
-//   - Instruction
-//   - Data
-//   - Relative
-//
-// In all cases, the `data_id` member is set to the id
-// of the WasmDatum entry that will be the base address,
-// and then the `offset` member will be added to that.
-//
-// In instruction patches, the `index` member is set
-// to the index of the function where the instruction should
-// be patched. The `location` member is set to the instruction
-// that needs to have its data changed.
-//
-// In data patches, the `index` member is set to the id
-// of the WasmDatum entry that needs to have a part of it
-// updated. The `location` member is the offset into the
-// data to update. It is assumed that 4 bytes will be reserved
-// to be replaced with the pointer value.
-//
-// In relative patches, `index` member is set to the id
-// of the WasmDatum entry that needs to have a part of it
-// updated. The `location` member is the offset into the
-// data to update. The difference between `Data` and `Relative`
-// is that `Relative` *adds* the base address to the current
-// value in the 4 bytes, as opposed to replacing it. As a
-// convenience, if the value is 0 (null), it will remain as
-// 0.
-//
-typedef struct DatumPatchInfo {
-    DatumPatchInfoKind kind;
-    u32 data_id;
-    u32 offset;
-    u32 location;
-    u32 index;
-} DatumPatchInfo;
-
-// Context used when building a constexpr buffer
-typedef struct ConstExprContext {
-   struct OnyxWasmModule *module;
-   ptr data;
-   u32 data_id;
-} ConstExprContext;
-
-typedef enum DeferredStmtType {
-    Deferred_Stmt_Node,
-    Deferred_Stmt_Code,
-} DeferredStmtType;
-
-typedef struct DeferredStmt {
-    DeferredStmtType type;
-    u32 depth;
-    AstDefer *defer_node;
-
-    union {
-        AstNode *stmt;
-        struct {
-            WasmInstruction* instructions;
-            u32 instruction_count;
-        };
-    };
-} DeferredStmt;
-
-typedef struct AllocatedSpace {
-    u64 depth;
-    AstTyped *expr;
-} AllocatedSpace;
-
-typedef struct StrLitInfo {
-    u32 data_id;
-    u32 len;
-} StrLitInfo;
-
-typedef struct PatchInfo {
-    u32 instruction_index;
-} PatchInfo;
-
-typedef struct ForRemoveInfo {
-    // These are WASM locals
-    u64 iterator_remove_func;
-    u64 iterator_data_ptr;
-
-    i32 remove_func_type_idx;
-} ForRemoveInfo;
-
-typedef struct OnyxWasmModule {
-    bh_allocator allocator;
-
-    bh_arena    *extended_instr_data;
-    bh_allocator extended_instr_alloc;
-
-    // NOTE: Mapping ptrs to function / global indicies
-    bh_imap index_map;
-
-    // NOTE: Mapping from local ast node ptrs to indicies or offsets, depending on the mode
-    bh_imap local_map;
-
-    i32 current_func_idx;
-    LocalAllocator* local_alloc;
-
-    // NOTE: Mapping ptrs to elements
-    bh_imap elem_map;
-
-    bh_arr(DeferredStmt)   deferred_stmts;
-    bh_arr(AllocatedSpace) local_allocations; 
-
-    bh_arr(PatchInfo) stack_leave_patches;
-    bh_arr(DatumPatchInfo) data_patches;
-
-    bh_arr(ForRemoveInfo) for_remove_info;
-
-    bh_arr(AstForeignBlock *) foreign_blocks;
-    u32 next_foreign_block_idx;
-
-    bh_arr(AstFunction *) procedures_with_tags;
-
-    // NOTE: Used internally as a map from strings that represent function types,
-    // 0x7f 0x7f : 0x7f ( (i32, i32) -> i32 )
-    // to the function type index if it has been created.
-    Table(i32) type_map;
-
-    Table(StrLitInfo) loaded_file_info;
-    Table(StrLitInfo) string_literals;
-
-    bh_arr(u8)  structured_jump_target;
-    bh_arr(AstLocal*) return_location_stack;   // NOTE: Used for do-block return expressions.
-
-    bh_arr(WasmFuncType*) types; // NOTE: This have to be pointers because the type is variadic in size
-    bh_arr(WasmImport)    imports;
-    Table(WasmExport)     exports;
-    bh_arr(WasmGlobal)    globals;
-    bh_arr(WasmFunc)      funcs;
-    bh_arr(WasmDatum)     data;
-    bh_arr(i32)           elems;
-    bh_arr(char *)        libraries;
-    bh_arr(char *)        library_paths;
-    b32 needs_memory_section;
-    u32 memory_min_size;
-    u32 memory_max_size;
-
-    // NOTE: Set of things used when compiling; not part of the actual module
-    u32 export_count;
-    u32 next_type_idx;
-    u32 next_func_idx;
-    u32 next_global_idx;
-    u32 next_tls_offset;
-    u32 next_elem_idx;
-    u32 foreign_function_count;
-
-    i32 *stack_top_ptr;
-    i32 *tls_size_ptr;
-    i32 *heap_start_ptr;
-    u64 stack_base_idx;
-    CallingConvention curr_cc;
-    i32 null_proc_func_idx;
-
-    b32 has_stack_locals : 1;
-
-#ifdef ENABLE_DEBUG_INFO
-    struct DebugContext *debug_context;
-#endif
-
-} OnyxWasmModule;
-
-typedef struct OnyxWasmLinkOptions {
-    b32 stack_first;
-    u32 stack_size;
-    u32 stack_alignment;
-
-    u32 null_reserve_size;
-
-    b32 import_memory;
-    char *import_memory_module_name;
-    char *import_memory_import_name;
-
-    b32 export_memory;
-    char *export_memory_name;
-
-    b32 export_func_table;
-    char *export_func_table_name;
-
-    u32 memory_min_size;
-    u32 memory_max_size;
-} OnyxWasmLinkOptions;
-
-b32 onyx_wasm_build_link_options_from_node(OnyxWasmLinkOptions *opts, struct AstTyped *node);
-
-OnyxWasmModule onyx_wasm_module_create(bh_allocator alloc);
-void onyx_wasm_module_link(OnyxWasmModule *module, OnyxWasmLinkOptions *options);
-void onyx_wasm_module_free(OnyxWasmModule* module);
-void onyx_wasm_module_write_to_buffer(OnyxWasmModule* module, bh_buffer* buffer);
-void onyx_wasm_module_write_to_file(OnyxWasmModule* module, bh_file file);
-
-#ifdef ENABLE_RUN_WITH_WASMER
-void onyx_run_initialize(b32 debug_enabled);
-b32 onyx_run_wasm(bh_buffer code_buffer, int argc, char *argv[]);
-#endif
-
-#ifdef ENABLE_DEBUG_INFO
-
-typedef enum DebugOpType {
-    DOT_END   = 0b00000000,
-    DOT_SET   = 0b00000001,
-    DOT_PUSHF = 0b00000010,
-    DOT_POPF  = 0b00000011,
-    DOT_SYM   = 0b00000100,
-
-    DOT_INC   = 0b01000000,
-    DOT_DEC   = 0b10000000,
-    DOT_REP   = 0b11000000,
-} DebugOpType;
-
-typedef struct DebugFuncContext {
-    u32 func_index;
-    u32 file_id;
-    u32 line;
-    u32 op_offset;
-    u32 stack_ptr_idx;
-
-    u32 name_length;
-    char *name;
-} DebugFuncContext;
-
-typedef struct DebugFileInfo {
-    u32 file_id;
-    u32 line_count;
-} DebugFileInfo;
-
-typedef enum DebugSymLoc {
-    DSL_REGISTER = 1,
-    DSL_STACK    = 2,
-    DSL_GLOBAL   = 3,
-} DebugSymLoc;
-
-typedef struct DebugSymInfo {
-    u32 sym_id;
-    u32 location_type;
-    u64 location_num;
-    char *name;
-    u32 type;
-} DebugSymInfo;
-
-typedef struct DebugSymPatch {
-    u32 func_idx;
-    u32 sym_id;
-    u64 local_idx;
-} DebugSymPatch;
-
-typedef struct DebugContext {
-    bh_allocator allocator;
-
-    Table(DebugFileInfo) file_info;
-    u32 next_file_id;
-
-    bh_arr(DebugSymInfo)  sym_info;
-    bh_arr(DebugSymPatch) sym_patches;
-    u32 next_sym_id;
-
-    bh_arr(DebugFuncContext) funcs;
-    bh_buffer                op_buffer;
-
-    // Used during building the debug info
-    OnyxToken *last_token;
-    b32 last_op_was_rep : 1;
-} DebugContext;
-
-#endif
-
-#endif
diff --git a/runtime/build.sh b/runtime/build.sh
new file mode 100755 (executable)
index 0000000..25cfb9a
--- /dev/null
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+. ../settings.sh
+
+$CC -shared -fpic -w \
+    -o ../bin/onyx_runtime.so \
+    -I ../shared/include -I ../compiler/include -I ../lib/common/include \
+    ./onyx_runtime.c \
+    -lpthread
+
+echo "Installing onyx_runtime.so"
+sudo mv "../bin/onyx_runtime.so" "$CORE_DIR/lib/onyx_runtime.so"
diff --git a/runtime/onyx_runtime.c b/runtime/onyx_runtime.c
new file mode 100644 (file)
index 0000000..88a1355
--- /dev/null
@@ -0,0 +1,1444 @@
+
+#define BH_DEFINE
+#include "bh.h"
+
+#define ONYX_LIBRARY_NAME onyx_runtime
+#define ONYX_NO_SHORT_NAMES
+#include "onyx_library.h"
+
+#ifdef _BH_LINUX
+    #include <pthread.h>
+    #include <signal.h>
+    #include <sys/wait.h>
+    #include <sys/types.h>
+    #include <dlfcn.h>
+    #include <dirent.h>
+    #include <arpa/inet.h>
+    #include <netdb.h>
+    #include <netinet/in.h>
+    #include <sys/socket.h>
+    #include <sys/un.h>
+    #include <poll.h>
+#endif
+
+#include "types.h"  // For POINTER_SIZE
+
+#define ONYX_FILE_ERROR_NONE 0
+#define ONYX_FILE_ERROR_NOT_FOUND 1
+#define ONYX_FILE_ERROR_EXISTS 2
+#define ONYX_FILE_ERROR_PERMISSION 3
+#define ONYX_FILE_ERROR_BAD_FILE 4
+#define ONYX_FILE_ERROR_BAD_MODE 5
+
+ONYX_DEF(__file_open_impl, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+    char *path_ptr = ONYX_PTR(params->data[0].of.i32);
+    int   path_len = params->data[1].of.i32;
+
+    char path[512] = {0};
+    path_len = bh_min(path_len, 511);
+    strncpy(path, path_ptr, path_len);
+    path[path_len] = 0;
+
+    int mode = params->data[2].of.i32;
+
+    bh_file_mode bh_mode;
+    switch (mode) {
+        case 1: bh_mode = BH_FILE_MODE_READ; break;
+        case 2: bh_mode = BH_FILE_MODE_WRITE; break;
+        case 3: bh_mode = BH_FILE_MODE_APPEND; break;
+    }
+
+    bh_file file;
+    bh_file_error error = bh_file_open_mode(&file, bh_mode, path);
+    if (error == BH_FILE_ERROR_INVALID) {
+        results->data[0] = WASM_I32_VAL(ONYX_FILE_ERROR_NOT_FOUND);
+        return NULL;
+    }
+
+    *(u64 *) ONYX_PTR(params->data[3].of.i32) = (u64) file.fd;
+    results->data[0] = WASM_I32_VAL(ONYX_FILE_ERROR_NONE);
+    return NULL;
+}
+
+ONYX_DEF(__file_close, (WASM_I64), (WASM_I32)) {
+    i64 fd = params->data[0].of.i64;
+
+    bh_file file = { (bh_file_descriptor) fd };
+    bh_file_error error = bh_file_close(&file);
+    if (error == BH_FILE_ERROR_INVALID) {
+        results->data[0] = WASM_I32_VAL(ONYX_FILE_ERROR_NOT_FOUND);
+        return NULL;
+    }
+
+    results->data[0] = WASM_I32_VAL(ONYX_FILE_ERROR_NONE);
+    return NULL;
+}
+
+ONYX_DEF(__file_exists, (WASM_I32, WASM_I32), (WASM_I32)) {
+    char *path_ptr = ONYX_PTR(params->data[0].of.i32);
+    int   path_len = params->data[1].of.i32;
+
+    char path[512] = {0};
+    path_len = bh_min(path_len, 511);
+    strncpy(path, path_ptr, path_len);
+    path[path_len] = 0;
+
+    results->data[0] = WASM_I32_VAL(bh_file_exists(path));
+    return NULL;
+}
+
+ONYX_DEF(__file_stat, (WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+    char *path_ptr = ONYX_PTR(params->data[0].of.i32);
+    int   path_len = params->data[1].of.i32;
+
+    char path[512] = {0};
+    path_len = bh_min(path_len, 511);
+    strncpy(path, path_ptr, path_len);
+    path[path_len] = 0;
+
+    results->data[0] = WASM_I32_VAL(bh_file_stat(path, ONYX_PTR(params->data[2].of.i32)));
+    return NULL;
+}
+
+ONYX_DEF(__file_remove, (WASM_I32, WASM_I32), (WASM_I32)) {
+    char *path_ptr = ONYX_PTR(params->data[0].of.i32);
+    int   path_len = params->data[1].of.i32;
+
+    char path[512] = {0};
+    path_len = bh_min(path_len, 511);
+    strncpy(path, path_ptr, path_len);
+    path[path_len] = 0;
+
+    results->data[0] = WASM_I32_VAL(bh_file_remove(path));
+    return NULL;
+}
+
+ONYX_DEF(__file_seek, (WASM_I64, WASM_I32, WASM_I32), (WASM_I32)) {
+    i64 fd = params->data[0].of.i64;
+    i32 offset = params->data[1].of.i32;
+    i32 whence = params->data[2].of.i32;
+
+    bh_file file = { (bh_file_descriptor) fd };
+    bh_file_whence bh_whence;
+    switch (whence) {
+        case 0: bh_whence = BH_FILE_WHENCE_BEGIN; break;
+        case 1: bh_whence = BH_FILE_WHENCE_CURRENT; break;
+        case 2: bh_whence = BH_FILE_WHENCE_END; break;
+    }
+
+    i64 new_offset = bh_file_seek(&file, offset, whence);
+    results->data[0] = WASM_I32_VAL((i32) new_offset);
+    return NULL;
+}
+
+ONYX_DEF(__file_tell, (WASM_I64), (WASM_I32)) {
+    i64 fd = params->data[0].of.i64;
+    bh_file file = { (bh_file_descriptor) fd };
+    results->data[0] = WASM_I32_VAL(bh_file_tell(&file));
+    return NULL;
+}
+
+ONYX_DEF(__file_read, (WASM_I64, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+    i64 fd = params->data[0].of.i64;
+    bh_file file = { (bh_file_descriptor) fd };
+
+    i32 curr_pos = bh_file_tell(&file);
+    b32 success = bh_file_read_at(&file,
+            bh_file_tell(&file),
+            ONYX_PTR(params->data[1].of.i32),
+            params->data[2].of.i32,
+            (i64 *) ONYX_PTR(params->data[3].of.i32));
+
+    bh_file_seek_to(&file, curr_pos + *(i32 *) ONYX_PTR(params->data[3].of.i32));
+
+    results->data[0] = WASM_I32_VAL(0);
+    if (!success) results->data[0] = WASM_I32_VAL(2);
+    return NULL;
+}
+
+ONYX_DEF(__file_write, (WASM_I64, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+    i64 fd = params->data[0].of.i64;
+    bh_file file = { (bh_file_descriptor) fd };
+
+    i32 curr_pos = bh_file_tell(&file);
+    b32 success = bh_file_write_at(&file,
+            bh_file_tell(&file),
+            ONYX_PTR(params->data[1].of.i32),
+            params->data[2].of.i32,
+            (i64 *) ONYX_PTR(params->data[3].of.i32));
+
+    bh_file_seek_to(&file, curr_pos + *(i32 *) ONYX_PTR(params->data[3].of.i32));
+
+    results->data[0] = WASM_I32_VAL(0);
+    if (!success) results->data[0] = WASM_I32_VAL(2);
+    return NULL;
+}
+
+ONYX_DEF(__file_flush, (WASM_I64), (WASM_I32)) {
+    i64 fd = params->data[0].of.i64;
+    bh_file file = { (bh_file_descriptor) fd };
+    bh_file_flush(&file);
+    results->data[0] = WASM_I32_VAL(0);
+    return NULL;
+}
+
+ONYX_DEF(__file_size, (WASM_I64), (WASM_I32)) {
+    i64 fd = params->data[0].of.i64;
+    bh_file file = { (bh_file_descriptor) fd };
+    results->data[0] = WASM_I32_VAL(bh_file_size(&file));
+    return NULL;
+}
+
+ONYX_DEF(__file_get_standard, (WASM_I32, WASM_I32), (WASM_I32)) {
+    bh_file_standard standard = (bh_file_standard) params->data[0].of.i32;
+
+    bh_file file;
+    bh_file_error error = bh_file_get_standard(&file, standard);
+    if (error == BH_FILE_ERROR_NONE) {
+        *(u64 *) ONYX_PTR(params->data[1].of.i32) = (u64) file.fd;
+    }
+
+    results->data[0] = WASM_I32_VAL(error == BH_FILE_ERROR_NONE);
+    return NULL;
+}
+
+ONYX_DEF(__file_rename, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+    char *old_path_ptr = ONYX_PTR(params->data[0].of.i32);
+    int   old_path_len = params->data[1].of.i32;
+
+    char old_path[512] = {0};
+    old_path_len = bh_min(old_path_len, 511);
+    strncpy(old_path, old_path_ptr, old_path_len);
+    old_path[old_path_len] = 0;
+
+    char *new_path_ptr = ONYX_PTR(params->data[2].of.i32);
+    int   new_path_len = params->data[3].of.i32;
+
+    char new_path[512] = {0};
+    new_path_len = bh_min(new_path_len, 511);
+    strncpy(new_path, new_path_ptr, new_path_len);
+    new_path[new_path_len] = 0;
+
+#ifdef _BH_WINDOWS
+    results->data[0] = WASM_I32_VAL(MoveFileA(old_path, new_path));
+    return NULL;
+#endif
+
+#ifdef _BH_LINUX
+    results->data[0] = WASM_I32_VAL(rename(old_path, new_path) == 0);
+    return NULL;
+#endif
+}
+
+//
+// Directories
+//
+
+ONYX_DEF(__dir_open, (WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+    char *path_ptr = ONYX_PTR(params->data[0].of.i32);
+    int   path_len = params->data[1].of.i32;
+
+    char path[512] = {0};
+    path_len = bh_min(path_len, 511);
+    strncpy(path, path_ptr, path_len);
+    path[path_len] = 0;
+
+#ifdef _BH_WINDOWS
+    for (int i=0; i<path_len; i++) if (path[i] == '/') path[i] = '\\';
+    strncat(path, "\\*.*", 511);
+
+    Windows_Directory_Opened* dir = malloc(sizeof(Windows_Directory_Opened));
+    dir->hndl = FindFirstFileA(path, &dir->found_file);
+    if (dir->hndl == INVALID_HANDLE_VALUE) {
+        results->data[0] = WASM_I32_VAL(0);
+        return NULL;
+    }
+
+    *(u64 *) ONYX_PTR(params->data[2].of.i32) = (u64) dir;
+
+    results->data[0] = WASM_I32_VAL(1);
+    return NULL;
+#endif
+
+#ifdef _BH_LINUX
+    DIR* dir = opendir(path);
+    *(u64 *) ONYX_PTR(params->data[2].of.i32) = (u64) dir;
+    results->data[0] = WASM_I32_VAL(dir != NULL);
+    return NULL;
+#endif
+}
+
+// (DIR*, PTR<DIRENT>) -> BOOL
+ONYX_DEF(__dir_read, (WASM_I64, WASM_I32), (WASM_I32)) {
+#ifdef _BH_WINDOWS
+    Windows_Directory_Opened* dir = (Windows_Directory_Opened *) params->data[0].of.i64;
+    if (dir == NULL) {
+        results->data[0] = WASM_I32_VAL(0);
+        return NULL;
+    }
+
+    do {
+        BOOL success = FindNextFileA(dir->hndl, &dir->found_file);
+        if (!success) {
+            results->data[0] = WASM_I32_VAL(0);
+            return NULL;
+        }
+    } while (!strcmp(dir->found_file.cFileName, ".") || !strcmp(dir->found_file.cFileName, ".."));
+
+    u32 out = params->data[1].of.i32;
+    assert(out != 0);
+
+    *(u32 *) ONYX_PTR(out + 0) = (dir->found_file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? 3 : 4;
+    *(u32 *) ONYX_PTR(out + 4) = 0;
+    *(u32 *) ONYX_PTR(out + 8) = strlen(dir->found_file.cFileName);
+    strncpy(ONYX_PTR(out + 12), dir->found_file.cFileName, 256);
+
+    results->data[0] = WASM_I32_VAL(1);
+    return NULL;
+#endif
+
+#ifdef _BH_LINUX
+    DIR* dir = (DIR *) params->data[0].of.i64;
+    if (dir == NULL) {
+        results->data[0] = WASM_I32_VAL(0);
+        return NULL;
+    }
+
+    struct dirent *ent;
+    while (1) {
+        ent = readdir(dir);
+        if (ent == NULL) {
+            results->data[0] = WASM_I32_VAL(0);
+            return NULL;
+        }
+
+        // Skip the current directory and parent directory
+        if (strcmp(ent->d_name, ".") && strcmp(ent->d_name, "..")) break;
+    }
+
+    u32 type = 0;
+    switch (ent->d_type) {
+        case DT_UNKNOWN: break;
+        case DT_BLK: type = 1; break;
+        case DT_CHR: type = 2; break;
+        case DT_DIR: type = 3; break;
+        case DT_LNK: type = 5; break;
+        case DT_REG: type = 4; break;
+        default: type = 6; break;
+    }
+
+    u32 out = params->data[1].of.i32;
+    assert(out != 0);
+
+    *(u32 *) ONYX_PTR(out + 0) = type;
+    *(u32 *) ONYX_PTR(out + 4) = (u32) ent->d_ino;
+    *(u32 *) ONYX_PTR(out + 8) = strlen(ent->d_name);
+    strncpy(ONYX_PTR(out + 12), ent->d_name, 256);
+
+    results->data[0] = WASM_I32_VAL(1);
+    return NULL;
+#endif
+}
+
+ONYX_DEF(__dir_close, (WASM_I64), ()) {
+#ifdef _BH_WINDOWS
+    Windows_Directory_Opened* dir = (Windows_Directory_Opened *) params->data[0].of.i64;
+
+    FindClose(dir->hndl);
+    free(dir);
+
+    return NULL;
+#endif
+
+#ifdef _BH_LINUX
+    DIR* dir = (DIR *) params->data[0].of.i64;
+    if (dir == NULL) return NULL;
+
+    closedir(dir);
+#endif
+    return NULL;
+}
+
+ONYX_DEF(__dir_create, (WASM_I32, WASM_I32), (WASM_I32)) {
+    char *path_ptr = ONYX_PTR(params->data[0].of.i32);
+    int   path_len = params->data[1].of.i32;
+
+    char path[512] = {0};
+    path_len = bh_min(path_len, 511);
+    strncpy(path, path_ptr, path_len);
+    path[path_len] = 0;
+
+#ifdef _BH_WINDOWS
+    results->data[0] = WASM_I32_VAL(CreateDirectoryA(path));
+    return NULL;
+#endif
+
+#ifdef _BH_LINUX
+    results->data[0] = WASM_I32_VAL(mkdir(path, 0777) == 0);
+    return NULL;
+#endif
+}
+
+ONYX_DEF(__dir_remove, (WASM_I32, WASM_I32), (WASM_I32)) {
+    char *path_ptr = ONYX_PTR(params->data[0].of.i32);
+    int   path_len = params->data[1].of.i32;
+
+    char path[512] = {0};
+    path_len = bh_min(path_len, 511);
+    strncpy(path, path_ptr, path_len);
+    path[path_len] = 0;
+
+#ifdef _BH_WINDOWS
+    results->data[0] = WASM_I32_VAL(RemoveDirectoryA(path));
+    return NULL;
+#endif
+
+#ifdef _BH_LINUX
+    results->data[0] = WASM_I32_VAL(rmdir(path) == 0);
+    return NULL;
+#endif
+}
+
+//
+// THREADS
+//
+typedef struct OnyxThread {
+    i32 id;
+    i32 tls_base;
+    i32 funcidx;
+    i32 dataptr;
+    wasm_instance_t* instance;
+
+    #ifdef _BH_LINUX
+        pthread_t thread;
+    #endif
+
+    #ifdef _BH_WINDOWS
+        HANDLE thread_handle;
+        i32    thread_id;
+    #endif
+} OnyxThread;
+
+static bh_arr(OnyxThread) threads = NULL;
+
+#ifdef _BH_LINUX
+static void *onyx_run_thread(void *data) {
+#endif
+#ifdef _BH_WINDOWS
+static i32 onyx_run_thread(void *data) {
+#endif
+    OnyxThread *thread = (OnyxThread *) data;
+
+    wasm_store_t *wasm_store = runtime->wasm_store_new(runtime->wasm_engine);
+
+    wasm_trap_t* traps = NULL;
+    thread->instance = runtime->wasm_instance_new(wasm_store, runtime->wasm_module, &runtime->wasm_imports, &traps);
+
+    wasm_extern_t* start_extern = runtime->wasm_extern_lookup_by_name(runtime->wasm_module, thread->instance, "_thread_start");
+    wasm_func_t*   start_func   = runtime->wasm_extern_as_func(start_extern);
+
+    wasm_extern_t* exit_extern = runtime->wasm_extern_lookup_by_name(runtime->wasm_module, thread->instance, "_thread_exit");
+    wasm_func_t*   exit_func   = runtime->wasm_extern_as_func(exit_extern);
+
+    wasm_trap_t* trap=NULL;
+
+    // NOTE: This is cached in a local variable because there is a tiny chance that if a lot of threads are created
+    // then the backing array for the thread handles will move and thread* we have well not be valid. I'm betting on this
+    // not happening before now in the function; however, it *could* happen before we call thread_exit. This would be bad
+    // because of the normal reasons why accessing memory that you don't own any more is bad.
+    i32 thread_id = thread->id;
+
+    { // Call the _thread_start procedure
+        wasm_val_t args[]    = { WASM_I32_VAL(thread_id), WASM_I32_VAL(thread->tls_base), WASM_I32_VAL(thread->funcidx), WASM_I32_VAL(thread->dataptr) };
+        wasm_val_vec_t results = { 0, 0 };
+        wasm_val_vec_t args_array = WASM_ARRAY_VEC(args);
+
+        trap = runtime->wasm_func_call(start_func, &args_array, &results);
+        if (trap != NULL) {
+            bh_printf("THREAD: %d\n", thread_id);
+            runtime->onyx_print_trap(trap);
+        }
+    }
+
+    { // Call the _thread_exit procedure
+        wasm_val_t args[]    = { WASM_I32_VAL(thread_id) };
+        wasm_val_vec_t results = { 0, 0 };
+        wasm_val_vec_t args_array = WASM_ARRAY_VEC(args);
+
+        trap = runtime->wasm_func_call(exit_func, &args_array, &results);
+    }
+
+    runtime->wasm_store_delete(wasm_store);
+
+    return 0;
+}
+
+ONYX_DEF(__spawn_thread, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+    if (threads == NULL) bh_arr_new(bh_heap_allocator(), threads, 128);
+    bh_arr_insert_end(threads, 1);
+    OnyxThread *thread = &bh_arr_last(threads);
+
+    thread->id       = params->data[0].of.i32;
+    thread->tls_base = params->data[1].of.i32;
+    thread->funcidx  = params->data[2].of.i32;
+    thread->dataptr  = params->data[3].of.i32;
+
+    #ifdef _BH_LINUX
+        pthread_create(&thread->thread, NULL, onyx_run_thread, thread);
+    #endif
+
+    #ifdef _BH_WINDOWS
+        // thread->thread_handle = CreateThread(NULL, 0, onyx_run_thread, thread, 0, &thread->thread_id);
+        thread->thread_handle = (HANDLE) _beginthreadex(NULL, 0, onyx_run_thread, thread, 0, &thread->thread_id);
+    #endif
+
+    results->data[0] = WASM_I32_VAL(1);
+    return NULL;
+}
+
+ONYX_DEF(__kill_thread, (WASM_I32), (WASM_I32)) {
+    i32 thread_id = params->data[0].of.i32;
+
+    i32 i = 0;
+    bh_arr_each(OnyxThread, thread, threads) {
+        if (thread->id == thread_id) {
+            #ifdef _BH_LINUX
+            // This leads to some weirdness and bugs...
+            //
+            pthread_kill(thread->thread, SIGKILL);
+            #endif
+
+            #ifdef _BH_WINDOWS
+            TerminateThread(thread->thread_handle, 0);
+            CloseHandle(thread->thread_handle);
+            #endif
+
+            bh_arr_deleten(threads, i, 1);
+            results->data[0] = WASM_I32_VAL(1);
+            return NULL;
+        }
+
+        i++;
+    }
+
+    results->data[0] = WASM_I32_VAL(0);
+    return NULL;
+}
+
+
+
+
+//
+// PROCESS
+//
+#define ONYX_PROCESS_MAGIC_NUMBER 0xdeadfadebabecafe
+typedef struct OnyxProcess {
+    u64 magic_number;
+
+#ifdef _BH_LINUX
+    // Pipes
+    i32 proc_to_host[2];
+    i32 host_to_proc[2];
+
+    pid_t pid;
+#endif
+
+#ifdef _BH_WINDOWS
+    HANDLE proc_to_host_read;
+    HANDLE proc_to_host_write;
+    HANDLE host_to_proc_read;
+    HANDLE host_to_proc_write;
+
+    PROCESS_INFORMATION proc_info;
+#endif
+} OnyxProcess;
+
+ONYX_DEF(__process_spawn, (WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I64)) {
+    char* process_str = ONYX_PTR(params->data[0].of.i32);
+    i32   process_len = params->data[1].of.i32;
+    i32   args_ptr    = params->data[2].of.i32;
+    i32   args_len    = params->data[3].of.i32;
+    b32   blocking_io = !params->data[4].of.i32;
+    char *cwd_str     = ONYX_PTR(params->data[5].of.i32);
+    i32   cwd_len     = params->data[6].of.i32;
+
+    char process_path[1024];
+    process_len = bh_min(1023, process_len);
+    memcpy(process_path, process_str, process_len);
+    process_path[process_len] = '\0';
+
+    char starting_dir[1024];
+    if (cwd_len > 0) {
+        cwd_len = bh_min(1023, cwd_len);
+        memcpy(starting_dir, cwd_str, cwd_len);
+        starting_dir[cwd_len] = '\0';
+    }
+
+    OnyxProcess *process = malloc(sizeof(OnyxProcess));
+    memset(process, 0, sizeof(*process));
+    process->magic_number = ONYX_PROCESS_MAGIC_NUMBER;
+
+    #ifdef _BH_LINUX
+        // :Security - alloca a user controlled string.
+        char **process_args = alloca(sizeof(char *) * (args_len + 2));
+        byte_t* array_loc = ONYX_PTR(args_ptr);
+        fori (i, 0, args_len) {
+            char *arg_str = ONYX_PTR(*(i32 *) (array_loc + i * 2 * POINTER_SIZE));
+            i32   arg_len = *(i32 *) (array_loc + i * 2 * POINTER_SIZE + POINTER_SIZE);
+
+            char *arg = alloca(sizeof(char) * (arg_len + 1));
+            memcpy(arg, arg_str, arg_len);
+            arg[arg_len] = '\0';
+            process_args[i + 1] = arg;
+        }
+        process_args[0] = process_path;
+        process_args[args_len + 1] = NULL;
+
+        if (pipe(process->proc_to_host) || pipe(process->host_to_proc)) {
+            wasm_val_init_ptr(&results->data[0], NULL); // Failed to run
+            return NULL;
+        }
+
+        pid_t pid;
+        switch (pid = fork()) {
+            case -1: // Bad fork
+                wasm_val_init_ptr(&results->data[0], NULL); // Failed to run
+
+                close(process->proc_to_host[0]);
+                close(process->proc_to_host[1]);
+                close(process->host_to_proc[0]);
+                close(process->host_to_proc[1]);
+                break;
+
+            case 0: // Child process
+                close(process->proc_to_host[0]);
+                close(process->host_to_proc[1]);
+                dup2(process->host_to_proc[0], 0); // Map the output to the pipe
+                dup2(process->proc_to_host[1], 1); // Map the output to the pipe
+                dup2(process->proc_to_host[1], 2); // Stderr to stdout
+
+                if (!blocking_io) {
+                    fcntl(0, F_SETFL, O_NONBLOCK);
+                    fcntl(1, F_SETFL, O_NONBLOCK);
+                }
+
+                if (cwd_len > 0) {
+                    chdir(starting_dir); // Switch current working directory.
+                }
+
+                execvp(process_path, process_args);
+                exit(-1);
+                break;
+
+            default: {
+                process->pid = pid;
+                close(process->host_to_proc[0]);
+                close(process->proc_to_host[1]);
+
+                wasm_val_init_ptr(&results->data[0], process);
+                break;
+            }
+        }
+
+    #endif
+
+    #ifdef _BH_WINDOWS
+        // CLEANUP CLEANUP CLEANUP: This is so freaking bad...
+        char cmdLine[2048];
+        memset(cmdLine, 0, 2048);
+        strncat(cmdLine, "\"", 2047);
+        strncat(cmdLine, process_path, 2047);
+        strncat(cmdLine, "\"", 2047);
+
+        byte_t* array_loc = ONYX_PTR(args_ptr);
+        fori (i, 0, args_len) {
+            char *arg_str = ONYX_PTR(*(i32 *) (array_loc + i * 2 * POINTER_SIZE));
+            i32   arg_len = *(i32 *) (array_loc + i * 2 * POINTER_SIZE + 4);
+
+            strncat(cmdLine, " \"", 2047);
+            strncat(cmdLine, arg_str, arg_len);
+            strncat(cmdLine, "\"", 2047);
+        }
+
+        STARTUPINFOA startup;
+        memset(&startup, 0, sizeof startup);
+        startup.cb = sizeof(startup);
+
+        SECURITY_ATTRIBUTES saAttr;
+        saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+        saAttr.lpSecurityDescriptor = NULL;
+        saAttr.bInheritHandle = 1;
+
+        i32 success = 1;
+        success = success && CreatePipe(&process->host_to_proc_read, &process->host_to_proc_write, &saAttr, 4096);
+        success = success && CreatePipe(&process->proc_to_host_read, &process->proc_to_host_write, &saAttr, 4096);
+        if (!success) {
+            // printf("FAILED TO CREATE PIPES: %d\n", GetLastError());
+            wasm_val_init_ptr(&results->data[0], NULL); // Failed to run @LEAK
+            return NULL;
+        }
+
+        success = SetHandleInformation(process->proc_to_host_read, 1 /* HANDLE_FLAG_INHERIT */, 0);
+        success = success && SetHandleInformation(process->host_to_proc_write, 1 /* HANDLE_FLAG_INHERIT */, 0);
+        if (!success) {
+            // printf("FAILED TO CONFIGURE PIPES: %d\n", GetLastError());
+            wasm_val_init_ptr(&results->data[0], NULL); // Failed to run @LEAK
+            return NULL;
+        }
+
+        startup.hStdInput  = process->host_to_proc_read;
+        startup.hStdOutput = process->proc_to_host_write;
+        startup.hStdError = process->proc_to_host_write;
+
+        startup.dwFlags |= STARTF_USESTDHANDLES;
+
+        memset(&process->proc_info, 0, sizeof process->proc_info);
+
+        char *working_dir = NULL;
+        if (cwd_len > 0) {
+            working_dir = starting_dir;
+        }
+
+        success = CreateProcessA(process_path, cmdLine, &saAttr, &saAttr, 1, 0, NULL, working_dir, &startup, &process->proc_info);
+        if (!success) {
+            printf("FAILED TO CREATE PROCESS: %d\n", GetLastError());
+            wasm_val_init_ptr(&results->data[0], NULL); // Failed to run @LEAK
+            return NULL;
+        }
+
+        CloseHandle(process->proc_to_host_write);
+        CloseHandle(process->host_to_proc_read);
+        wasm_val_init_ptr(&results->data[0], process);
+    #endif
+
+    return NULL;
+}
+
+ONYX_DEF(__process_read, (WASM_I64, WASM_I32, WASM_I32), (WASM_I32)) {
+    OnyxProcess *process = (OnyxProcess *) params->data[0].of.i64;
+    if (process == NULL || process->magic_number != ONYX_PROCESS_MAGIC_NUMBER) {
+        results->data[0] = WASM_I32_VAL(0);
+        return NULL;
+    }
+
+    i32 output_ptr = params->data[1].of.i32;
+    i32 output_len = params->data[2].of.i32;
+    u8 *buffer = ONYX_PTR(output_ptr);
+
+    i32 bytes_read;
+    #ifdef _BH_LINUX
+        bytes_read = read(process->proc_to_host[0], buffer, output_len);
+        if (bytes_read < 0) {
+            switch (errno) {
+                case EAGAIN: bytes_read =  0; break;
+                case EBADF:  bytes_read = -1; break;
+                default:     bytes_read = -2; break;
+            }
+        }
+    #endif
+
+    #ifdef _BH_WINDOWS
+        i32 success = ReadFile(process->proc_to_host_read, buffer, output_len, &bytes_read, NULL);
+        if (!success) {
+            bytes_read = -1;
+        }
+    #endif
+
+    results->data[0] = WASM_I32_VAL(bytes_read);
+    return NULL;
+}
+
+ONYX_DEF(__process_write, (WASM_I64, WASM_I32, WASM_I32), (WASM_I32)) {
+    OnyxProcess *process = (OnyxProcess *) params->data[0].of.i64;
+    if (process == NULL || process->magic_number != ONYX_PROCESS_MAGIC_NUMBER) {
+        results->data[0] = WASM_I32_VAL(0);
+        return NULL;
+    }
+
+    i32 input_ptr = params->data[1].of.i32;
+    i32 input_len = params->data[2].of.i32;
+    u8 *buffer = ONYX_PTR(input_ptr);
+
+    i32 bytes_written;
+    #ifdef _BH_LINUX
+        bytes_written = write(process->host_to_proc[1], buffer, input_len);
+        bytes_written = bh_max(bytes_written, 0);  // Silently consume errors
+    #endif
+
+    #ifdef _BH_WINDOWS
+        i32 success = WriteFile(process->host_to_proc_write, buffer, input_len, &bytes_written, NULL);
+        if (!success) bytes_written = 0;
+    #endif
+
+    results->data[0] = WASM_I32_VAL(bytes_written);
+    return NULL;
+}
+
+ONYX_DEF(__process_kill, (WASM_I64), (WASM_I32)) {
+    OnyxProcess *process = (OnyxProcess *) params->data[0].of.i64;
+    if (process == NULL || process->magic_number != ONYX_PROCESS_MAGIC_NUMBER) {
+        results->data[0] = WASM_I32_VAL(0);
+        return NULL;
+    }
+
+    #ifdef _BH_LINUX
+        i32 failed = kill(process->pid, SIGKILL);
+        results->data[0] = WASM_I32_VAL(!failed);
+    #endif
+
+    #ifdef _BH_WINDOWS
+        i32 success = TerminateProcess(process->proc_info.hProcess, 1);
+        results->data[0] = WASM_I32_VAL(success ? 1 : 0);
+    #endif
+
+    return NULL;
+}
+
+ONYX_DEF(__process_wait, (WASM_I64), (WASM_I32)) {
+    OnyxProcess *process = (OnyxProcess *) params->data[0].of.i64;
+    if (process == NULL || process->magic_number != ONYX_PROCESS_MAGIC_NUMBER) {
+        results->data[0] = WASM_I32_VAL(1);
+        return NULL;
+    }
+
+    #ifdef _BH_LINUX
+        i32 status;
+        waitpid(process->pid, &status, 0);
+
+        i32 exit_code = WEXITSTATUS(status);
+        results->data[0] = WASM_I32_VAL(exit_code != 0 ? 2 : 0);
+    #endif
+
+    #ifdef _BH_WINDOWS
+        DWORD exitCode;
+        while (1) {
+            if (!WaitForSingleObject(process->proc_info.hProcess, INFINITE)) {
+                // HACK HACK HACK
+                DWORD error = GetLastError();
+                if (error != 109 && error != 6) {
+                    // printf("ERROR IN WAIT FOR SINGLE: %d\n", error);
+                    results->data[0] = WASM_I32_VAL(1);
+                    return NULL;
+                }
+            }
+
+            if (!GetExitCodeProcess(process->proc_info.hProcess, &exitCode)) {
+                // HACK HACK HACK
+                // Apparently, I'm doing something wrong (maybe?) where the process handle becomes
+                // invalid and causes error 6 "invalid handle". So I think I can safely assume that
+                // if that is the case, then the process exited? probably successfuly? hopefully?
+                // Honestly I don't know and I can't find any documentation describing when a process
+                // handle goes invalid, other than after you close it explicitly. But in the run_tests
+                // script, I'm not calling either process_kill or process_destroy, which are the only
+                // other functions that close the process handle. So I'm left in the dark as to why this
+                // is happening, but oh well. This works for now.
+                //                                                           - brendanfh 2021/12/03
+                if (GetLastError() == 6) {
+                    exitCode = 0;
+                    break;
+                }
+
+                results->data[0] = WASM_I32_VAL(3);
+                return NULL;
+            }
+
+            // 259 is STILL_ACTIVE (aka STATUS_PENDING), which means that the process has not yet exited
+            if (exitCode != 259) break;
+        }
+
+        results->data[0] = WASM_I32_VAL(exitCode != 0 ? 2 : 0);
+    #endif
+
+    return NULL;
+}
+
+ONYX_DEF(__process_destroy, (WASM_I64), ()) {
+    OnyxProcess *process = (OnyxProcess *) params->data[0].of.i64;
+    if (process == NULL || process->magic_number != ONYX_PROCESS_MAGIC_NUMBER) {
+        return NULL;
+    }
+
+    #ifdef _BH_LINUX
+        close(process->proc_to_host[0]);
+        close(process->host_to_proc[1]);
+    #endif
+
+    #ifdef _BH_WINDOWS
+        if (!CloseHandle(process->proc_info.hThread)
+         || !CloseHandle(process->proc_info.hProcess)) {
+            // printf("ERROR CLOSING HANDLES: %d\n", GetLastError());
+         }
+    #endif
+
+    free(process);
+
+    return NULL;
+}
+
+ONYX_DEF(__args_get, (WASM_I32, WASM_I32), ()) {
+    if (runtime->argc == 0 || runtime->argv == NULL) {
+        return NULL;
+    }
+
+    i32 buffer_base = params->data[1].of.i32;
+
+    for (int i=0; i<runtime->argc; i++) {
+        // Should this be strncpy? What would the length be?
+        strcpy(ONYX_PTR(buffer_base), runtime->argv[i]);
+        *(i32 *) ONYX_PTR(params->data[0].of.i32 + i * 4) = buffer_base;
+        buffer_base += strlen(runtime->argv[i]) + 1;
+    }
+
+    return NULL;
+}
+
+ONYX_DEF(__args_sizes_get, (WASM_I32, WASM_I32), ()) {
+    *(i32 *) ONYX_PTR(params->data[0].of.i32) = runtime->argc;
+
+    i32 argv_size = 0;
+    for (int i=0; i<runtime->argc; i++) {
+        argv_size += strlen(runtime->argv[i]) + 1;
+    }
+
+    *(i32 *) ONYX_PTR(params->data[1].of.i32) = argv_size;
+    return NULL;
+}
+
+ONYX_DEF(__exit, (WASM_I32), ()) {
+    exit(params->data[0].of.i32);
+    return NULL;
+}
+
+ONYX_DEF(__sleep, (WASM_I32), ()) {
+    #ifdef _BH_LINUX
+    usleep(params->data[0].of.i32 * 1000);
+    #endif
+
+    #ifdef _BH_WINDOWS
+    Sleep(params->data[0].of.i32);
+    #endif
+    return NULL;
+}
+
+ONYX_DEF(__time, (), (WASM_I64)) {
+    results->data[0] = WASM_I64_VAL(bh_time_curr());
+    return NULL;
+}
+
+
+
+
+//
+// Dates and Times
+//
+ONYX_DEF(__time_localtime, (WASM_I64, WASM_I32), ()) {
+    u64 t = params->data[0].of.i64;
+    *(struct tm *) ONYX_PTR(params->data[1].of.i32) = *localtime(&t);
+    return NULL;
+}
+
+ONYX_DEF(__time_gmtime, (WASM_I64, WASM_I32), ()) {
+    u64 t = params->data[0].of.i64;
+    *(struct tm *) ONYX_PTR(params->data[1].of.i32) = *gmtime(&t);
+    return NULL;
+}
+
+ONYX_DEF(__time_strftime, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+    u32 len = strftime(ONYX_PTR(params->data[0].of.i32), params->data[1].of.i32, ONYX_PTR(params->data[2].of.i32), ONYX_PTR(params->data[3].of.i32));
+    results->data[0] = WASM_I32_VAL(len); 
+    return NULL;
+}
+
+ONYX_DEF(__time_strptime, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+    #if defined(_BH_LINUX)
+    char *rem = strptime(ONYX_PTR(params->data[0].of.i32), params->data[1].of.i32, ONYX_PTR(params->data[2].of.i32), ONYX_PTR(params->data[3].of.i32));
+    results->data[0] = WASM_I32_VAL(rem != NULL); 
+    #else
+    results->data[0] = WASM_I32_VAL(0);
+    #endif
+    return NULL;
+}
+
+
+
+//
+// Networking
+//
+struct onyx_socket_addr {
+    unsigned short family;
+    unsigned short port;
+    unsigned int   addr;
+};
+
+static inline int onyx_socket_domain(int i) {
+    #if defined(_BH_LINUX)
+    switch (i) {    // :EnumDependent
+        case 0: return AF_UNIX;
+        case 1: return AF_INET;
+        case 2: return AF_INET6;
+        default: return -1;
+    }
+    #elif defined(_BH_WINDOWS)
+    return -1;
+    #endif
+}
+
+static inline int onyx_socket_protocol(int i) {
+    #if defined(_BH_LINUX)
+    switch (i) {    // :EnumDependent
+        case 0: return SOCK_STREAM;
+        case 1: return SOCK_DGRAM;
+        default: return -1;
+    }
+    #elif defined(_BH_WINDOWS)
+    return -1;
+    #endif
+}
+
+ONYX_DEF(__net_create_socket, (WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+
+    #ifdef _BH_LINUX
+    int domain = onyx_socket_domain(params->data[1].of.i32);
+    if (domain == -1) goto bad_settings;
+
+    int type = onyx_socket_protocol(params->data[2].of.i32);
+    if (type == -1) goto bad_settings;
+
+    *((int *) ONYX_PTR(params->data[0].of.i32)) = socket(domain, type, 0);
+
+    results->data[0] = WASM_I32_VAL(0);
+    return NULL;
+    #endif 
+
+    #ifdef _BH_WINDOWS
+    #endif
+
+bad_settings:
+    results->data[0] = WASM_I32_VAL(1); // :EnumDependent
+    return NULL;
+}
+
+ONYX_DEF(__net_close_socket, (WASM_I32), ()) {
+    #ifdef _BH_LINUX
+    shutdown(params->data[0].of.i32, SHUT_RDWR);
+    close(params->data[0].of.i32);
+    #endif
+
+    #ifdef _BH_WINDOWS
+    #endif
+
+    return NULL;
+}
+
+ONYX_DEF(__net_setting, (WASM_I32, WASM_I32, WASM_I32), ()) {
+    #ifdef _BH_LINUX
+    switch (params->data[1].of.i32) {
+        case 1: { // :EnumDependent  Non-Blocking
+            int s = params->data[0].of.i32;
+            int flags = fcntl(s, F_GETFL, 0);
+            if (params->data[2].of.i32) {
+                flags |= O_NONBLOCK;
+            } else {
+                flags &= ~O_NONBLOCK;
+            }
+
+            fcntl(s, F_SETFL, flags);
+            break;
+        }
+
+        case 2: { // :EnumDependent  Broadcast
+            int s = params->data[0].of.i32;
+            setsockopt(s, SOL_SOCKET, SO_BROADCAST, (void *) &params->data[2].of.i32, sizeof(int));
+            break;
+        }
+    }
+    #endif
+
+    return NULL;
+}
+
+ONYX_DEF(__net_bind, (WASM_I32, WASM_I32), (WASM_I32)) {
+
+    #ifdef _BH_LINUX
+    int res = -1;
+
+    struct onyx_socket_addr *oaddr = (void *) ONYX_PTR(params->data[1].of.i32);
+    int family = onyx_socket_domain(oaddr->family);
+    int port   = oaddr->port;
+
+    switch (family) {
+        case AF_INET: {
+            struct sockaddr_in bind_addr;
+            memset(&bind_addr, 0, sizeof(bind_addr));
+
+            bind_addr.sin_family = AF_INET;
+            bind_addr.sin_addr.s_addr = htonl(oaddr->addr);
+            bind_addr.sin_port = htons(port);
+
+            res = bind(params->data[0].of.i32, &bind_addr, sizeof(bind_addr));
+            break;
+        }
+
+        case AF_INET6: {
+            struct sockaddr_in6 bind_addr;
+            memset(&bind_addr, 0, sizeof(bind_addr));
+
+            bind_addr.sin6_family = AF_INET6;
+            memcpy(&bind_addr.sin6_addr.s6_addr, (void *) &oaddr->addr, 16);
+            bind_addr.sin6_port = htons(port);
+            
+            res = bind(params->data[0].of.i32, &bind_addr, sizeof(bind_addr));
+            break;
+        }
+
+        case AF_UNIX: {
+            struct sockaddr_un bind_addr;
+            memset(&bind_addr, 0, sizeof(bind_addr));
+
+            bind_addr.sun_family = AF_UNIX;
+            strncpy(&bind_addr.sun_path, (char *) &oaddr->addr, 108);
+            
+            res = bind(params->data[0].of.i32, &bind_addr, sizeof(bind_addr));
+            break;
+        }
+    }
+
+    results->data[0] = WASM_I32_VAL(res >= 0);
+    #endif
+
+    #ifdef _BH_WINDOWS
+    #endif
+
+    return NULL;
+}
+
+ONYX_DEF(__net_listen, (WASM_I32, WASM_I32), ()) {
+    #ifdef _BH_LINUX
+    listen(params->data[0].of.i32, params->data[1].of.i32);
+    #endif
+
+    #ifdef _BH_WINDOWS
+    #endif
+
+    return NULL;
+}
+
+ONYX_DEF(__net_accept, (WASM_I32, WASM_I32), (WASM_I32)) {
+    #ifdef _BH_LINUX
+    struct sockaddr_in client_addr;
+    int client_len = sizeof(client_addr);
+    memset(&client_addr, 0, client_len);
+
+    int client_socket = accept(params->data[0].of.i32, &client_addr, &client_len);
+
+    struct onyx_socket_addr* out_addr = (struct onyx_socket_addr *) ONYX_PTR(params->data[1].of.i32); 
+    out_addr->family = client_addr.sin_family;
+    out_addr->port   = ntohs(client_addr.sin_port);
+    out_addr->addr   = ntohl(client_addr.sin_addr.s_addr);
+
+    results->data[0] = WASM_I32_VAL(client_socket);
+    #endif
+
+    return NULL;
+}
+
+ONYX_DEF(__net_connect_unix, (WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+    #ifdef _BH_LINUX
+    int   hostlen  = params->data[2].of.i32;
+    char *hostname = alloca(hostlen + 1);
+    memcpy(hostname, ONYX_PTR(params->data[1].of.i32), hostlen);
+    hostname[hostlen] = '\0';
+
+    struct sockaddr_un server_addr;
+    memset(&server_addr, 0, sizeof(server_addr));
+
+    server_addr.sun_family = AF_UNIX; // See comment above
+    memcpy((char *)&server_addr.sun_path, hostname, hostlen);
+
+    int result = connect(params->data[0].of.i32, &server_addr, sizeof(server_addr));
+    if (result == 0) results->data[0] = WASM_I32_VAL(0);
+    else             results->data[0] = WASM_I32_VAL(3); // :EnumDependent
+    #endif
+    
+    return NULL;
+}
+
+ONYX_DEF(__net_connect_ipv4, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+    #ifdef _BH_LINUX
+    int   hostlen  = params->data[2].of.i32;
+    char *hostname = alloca(hostlen + 1);
+    memcpy(hostname, ONYX_PTR(params->data[1].of.i32), hostlen);
+    hostname[hostlen] = '\0';
+
+    struct hostent *host;
+    host = gethostbyname(hostname); // TODO: Replace this call, as it is obselete.
+    if (host == NULL) {
+        results->data[0] = WASM_I32_VAL(2);  // :EnumDependent
+        return NULL;
+    }
+
+    struct sockaddr_in server_addr;
+    memset(&server_addr, 0, sizeof(server_addr));
+
+    server_addr.sin_family = AF_INET; // See comment above
+    memcpy((char *)&server_addr.sin_addr.s_addr, (char *)host->h_addr, host->h_length);
+    server_addr.sin_port = htons(params->data[3].of.i32);
+
+    int result = connect(params->data[0].of.i32, &server_addr, sizeof(server_addr));
+    if (result == 0) results->data[0] = WASM_I32_VAL(0);
+    else             results->data[0] = WASM_I32_VAL(3); // :EnumDependent
+
+    return NULL;
+    #endif
+
+    #ifdef _BH_WINDOWS
+    #endif
+}
+
+ONYX_DEF(__net_send, (WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+    #ifdef _BH_LINUX
+    // TODO: The flags at the end should be controllable.
+    int sent = send(params->data[0].of.i32, ONYX_PTR(params->data[1].of.i32), params->data[2].of.i32, MSG_NOSIGNAL);
+    results->data[0] = WASM_I32_VAL(sent);
+    #endif
+    
+    return NULL;
+}
+
+ONYX_DEF(__net_sendto, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+    #ifdef _BH_LINUX
+    struct sockaddr_in dest_addr;
+    int dest_addr_len = sizeof(dest_addr);
+    memset(&dest_addr, 0, dest_addr_len);
+
+    struct onyx_socket_addr *o_addr = (struct onyx_socket_addr *) ONYX_PTR(params->data[3].of.i32);
+    dest_addr.sin_family = AF_INET; // TODO: See other comments related to AF_NET above.
+    dest_addr.sin_port = htons(o_addr->port);
+    dest_addr.sin_addr.s_addr = htonl(o_addr->addr);
+
+    // TODO: The flags at the end should be controllable.
+    int sent = sendto(params->data[0].of.i32, ONYX_PTR(params->data[1].of.i32), params->data[2].of.i32, MSG_NOSIGNAL, &dest_addr, dest_addr_len);
+    results->data[0] = WASM_I32_VAL(sent);
+    #endif
+    
+    return NULL;
+}
+
+ONYX_DEF(__net_recv, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+    *(i32 *) ONYX_PTR(params->data[3].of.i32) = 0;
+
+    #ifdef _BH_LINUX
+    // TODO: The flags at the end should be controllable.
+    int received = recv(params->data[0].of.i32, ONYX_PTR(params->data[1].of.i32), params->data[2].of.i32, 0);
+    results->data[0] = WASM_I32_VAL(received);
+
+    if (received < 0) {
+        if (errno == EAGAIN || errno == EWOULDBLOCK) {
+            *(i32 *) ONYX_PTR(params->data[3].of.i32) = 1;
+        }
+    }
+    #endif
+
+    return NULL;
+}
+
+ONYX_DEF(__net_recvfrom, (WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+    *(i32 *) ONYX_PTR(params->data[4].of.i32) = 0;
+
+    #ifdef _BH_LINUX
+    struct onyx_socket_addr *out_addr = (struct onyx_socket_addr *) ONYX_PTR(params->data[3].of.i32);
+
+    struct sockaddr_in client_addr;
+    int socket_len = sizeof(client_addr);
+    memset(&client_addr, 0, socket_len);
+
+    int received = recvfrom(params->data[0].of.i32, ONYX_PTR(params->data[1].of.i32), params->data[2].of.i32, 0, &client_addr, &socket_len);
+    out_addr->family = client_addr.sin_family;
+    out_addr->port   = ntohs(client_addr.sin_port);
+    out_addr->addr   = ntohl(client_addr.sin_addr.s_addr);
+
+    results->data[0] = WASM_I32_VAL(received);
+
+    if (received < 0) {
+        if (errno == EAGAIN || errno == EWOULDBLOCK) {
+            *(i32 *) ONYX_PTR(params->data[3].of.i32) = 1;
+        }
+    }
+    #endif
+
+    return NULL;
+}
+
+ONYX_DEF(__net_poll_recv, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), ()) {
+    #ifdef _BH_LINUX
+    int i, res, cursor;
+    struct pollfd* fds;
+
+    fds = alloca(params->data[1].of.i32 * sizeof(struct pollfd));
+
+    for (i=0; i<params->data[1].of.i32; i++) {
+        fds[i].fd = *(i32 *) ONYX_PTR(params->data[0].of.i32 + 4 * i);
+        fds[i].events = POLLIN;
+        fds[i].revents = 0;
+    }
+
+    res = poll(fds, params->data[1].of.i32, params->data[2].of.i32);
+
+    for (i=0; i<params->data[1].of.i32; i++) {
+        *(i32 *) ONYX_PTR(params->data[3].of.i32 + 4 * i) = 0; // NO_CHANGE
+
+        if (fds[i].revents & POLLIN) {
+            *(i32 *) ONYX_PTR(params->data[3].of.i32 + 4 * i) = 1; // READABLE
+        }
+
+        if ((fds[i].revents & POLLHUP)
+            || (fds[i].revents & POLLNVAL)
+            || (fds[i].revents & POLLERR)) {
+            *(i32 *) ONYX_PTR(params->data[3].of.i32 + 4 * i) = 2; // CLOSED
+        }
+    }
+
+    #endif
+
+    return NULL;
+}
+
+ONYX_DEF(__net_host_to_net_s, (WASM_I32), (WASM_I32)) {
+    results->data[0] = WASM_I32_VAL(htons(params->data[0].of.i32));
+    return NULL;
+}
+
+ONYX_DEF(__net_host_to_net_l, (WASM_I32), (WASM_I32)) {
+    results->data[0] = WASM_I32_VAL(htonl(params->data[0].of.i32));
+    return NULL;
+}
+
+ONYX_DEF(__net_net_to_host_s, (WASM_I32), (WASM_I32)) {
+    results->data[0] = WASM_I32_VAL(ntohs(params->data[0].of.i32));
+    return NULL;
+}
+
+ONYX_DEF(__net_net_to_host_l, (WASM_I32), (WASM_I32)) {
+    results->data[0] = WASM_I32_VAL(ntohl(params->data[0].of.i32));
+    return NULL;
+}
+
+
+//
+// C-Pointers
+//
+// These are wildly unsafe and break the core principles of the security
+// of WebAssembly, so there should be a way to turn them off!
+//
+ONYX_DEF(__cptr_make, (WASM_I32), (WASM_I64)) {
+    wasm_val_init_ptr(&results->data[0], ONYX_PTR(params->data[0].of.i32));
+    return NULL;
+}
+
+ONYX_DEF(__cptr_read, (WASM_I64, WASM_I32, WASM_I32), ()) {
+    memcpy(ONYX_PTR(params->data[1].of.i32), (void *) params->data[0].of.i64, params->data[2].of.i32);
+    return NULL;
+}
+
+ONYX_DEF(__cptr_read_u8, (WASM_I64), (WASM_I32)) {
+    results->data[0] = WASM_I32_VAL(*(u8 *) params->data[0].of.i64);
+    return NULL;
+}
+
+ONYX_DEF(__cptr_read_u16, (WASM_I64), (WASM_I32)) {
+    results->data[0] = WASM_I32_VAL(*(u16 *) params->data[0].of.i64);
+    return NULL;
+}
+
+ONYX_DEF(__cptr_read_u32, (WASM_I64), (WASM_I32)) {
+    results->data[0] = WASM_I32_VAL(*(u32 *) params->data[0].of.i64);
+    return NULL;
+}
+
+ONYX_DEF(__cptr_read_u64, (WASM_I64), (WASM_I64)) {
+    results->data[0] = WASM_I64_VAL(*(u64 *) params->data[0].of.i64);
+    return NULL;
+}
+
+ONYX_DEF(__cptr_extract_str, (WASM_I64, WASM_I32, WASM_I32), (WASM_I32)) {
+    unsigned int len = strlen((char *) params->data[0].of.i64);
+
+    if (params->data[2].of.i32 != 0) {
+        strncpy(ONYX_PTR(params->data[1].of.i32), (char *) params->data[0].of.i64, params->data[2].of.i32);
+    }
+
+    results->data[0] = WASM_I32_VAL(len);
+    return NULL;
+}
+
+ONYX_LIBRARY {
+    ONYX_FUNC(__file_open_impl)
+    ONYX_FUNC(__file_close)
+    ONYX_FUNC(__file_exists)
+    ONYX_FUNC(__file_stat)
+    ONYX_FUNC(__file_remove)
+    ONYX_FUNC(__file_seek)
+    ONYX_FUNC(__file_tell)
+    ONYX_FUNC(__file_read)
+    ONYX_FUNC(__file_write)
+    ONYX_FUNC(__file_flush)
+    ONYX_FUNC(__file_size)
+    ONYX_FUNC(__file_get_standard)
+    ONYX_FUNC(__file_rename)
+
+    ONYX_FUNC(__dir_open)
+    ONYX_FUNC(__dir_read)
+    ONYX_FUNC(__dir_close)
+    ONYX_FUNC(__dir_create)
+    ONYX_FUNC(__dir_remove)
+
+    ONYX_FUNC(__spawn_thread)
+    ONYX_FUNC(__kill_thread)
+
+    ONYX_FUNC(__process_spawn)
+    ONYX_FUNC(__process_read)
+    ONYX_FUNC(__process_write)
+    ONYX_FUNC(__process_kill)
+    ONYX_FUNC(__process_wait)
+    ONYX_FUNC(__process_destroy)
+
+    ONYX_FUNC(__args_get)
+    ONYX_FUNC(__args_sizes_get)
+
+    ONYX_FUNC(__exit)
+    ONYX_FUNC(__sleep)
+    ONYX_FUNC(__time)
+
+    ONYX_FUNC(__time_localtime)
+    ONYX_FUNC(__time_gmtime)
+    ONYX_FUNC(__time_strftime)
+    ONYX_FUNC(__time_strptime)
+
+    ONYX_FUNC(__net_create_socket)
+    ONYX_FUNC(__net_close_socket)
+    ONYX_FUNC(__net_setting)
+    ONYX_FUNC(__net_bind)
+    ONYX_FUNC(__net_listen)
+    ONYX_FUNC(__net_accept)
+    ONYX_FUNC(__net_connect_unix)
+    ONYX_FUNC(__net_connect_ipv4)
+    ONYX_FUNC(__net_send)
+    ONYX_FUNC(__net_sendto)
+    ONYX_FUNC(__net_recv)
+    ONYX_FUNC(__net_recvfrom)
+    ONYX_FUNC(__net_poll_recv)
+    ONYX_FUNC(__net_host_to_net_s)
+    ONYX_FUNC(__net_host_to_net_l)
+    ONYX_FUNC(__net_net_to_host_s)
+    ONYX_FUNC(__net_net_to_host_l)
+
+    ONYX_FUNC(__cptr_make)
+    ONYX_FUNC(__cptr_read)
+    ONYX_FUNC(__cptr_read_u8)
+    ONYX_FUNC(__cptr_read_u16)
+    ONYX_FUNC(__cptr_read_u32)
+    ONYX_FUNC(__cptr_read_u64)
+    ONYX_FUNC(__cptr_extract_str)
+
+    NULL
+};
diff --git a/settings.sh b/settings.sh
new file mode 100644 (file)
index 0000000..7ab78ec
--- /dev/null
@@ -0,0 +1,24 @@
+
+# The base path for the installation.
+# This is typcially /usr or /usr/local.
+INSTALL_DIR="/usr"
+
+# Where the core libraries for Onyx will go. 
+CORE_DIR="$INSTALL_DIR/share/onyx"
+
+# Where the onyx executable will be placed.
+BIN_DIR="$INSTALL_DIR/bin"
+
+# The compiler to use. Only GCC and TCC have been tested.
+CC='gcc'
+
+# The architecture of your system. If your not sure, leave this alone.
+ARCH="$(uname -m)"
+
+RUNTIME_LIBRARY="ovmwasm"
+# RUNTIME_LIBRARY="wasmer"
+
+# Where the Wasmer library files can be found.
+# They are bundled with the project, but if a different version is available, these can be changed.
+WASMER_INCLUDE_DIR="$(pwd)/lib/common/include"
+WASMER_LIBRARY_DIR="$(pwd)/lib/linux_$ARCH/lib"
diff --git a/shared/include/bh.h b/shared/include/bh.h
new file mode 100644 (file)
index 0000000..0049b26
--- /dev/null
@@ -0,0 +1,2787 @@
+#ifndef BH_H
+#define BH_H
+
+// NOTE: For lseek64
+#define _LARGEFILE64_SOURCE
+
+#if defined(_WIN32) || defined(_WIN64)
+    #define _BH_WINDOWS 1
+#endif
+
+#if defined(__unix__)
+    #define _BH_LINUX 1
+#endif
+
+#include <sys/stat.h>
+#include <malloc.h>
+#include <time.h>
+
+#ifdef _BH_LINUX
+    #include <errno.h>
+    #include <fcntl.h>
+    #include <unistd.h>
+    #include <dirent.h>
+#endif
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h> // TODO: Replace with needed functions
+#include <stdint.h>
+#include <assert.h>
+#include <stdio.h>
+
+#if defined(_MSC_VER) && !defined(_WINDOWS_)
+    #include "small_windows.h"
+#endif
+
+//-------------------------------------------------------------------------------------
+// Better types
+//-------------------------------------------------------------------------------------
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+typedef int8_t i8;
+typedef int16_t i16;
+typedef int32_t i32;
+typedef int64_t i64;
+typedef int64_t isize;
+typedef i32 b32;
+typedef void* ptr;
+typedef float f32;
+typedef double f64;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Better character functions
+//-------------------------------------------------------------------------------------
+b32 char_is_alpha(const char a);
+b32 char_is_num(const char a);
+b32 char_is_alphanum(const char a);
+char charset_contains(const char* charset, char ch);
+b32 char_is_whitespace(const char a);
+b32 char_in_range(const char lo, const char hi, const char a);
+i64 chars_match(char* ptr1, char* ptr2);
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Better math functions
+//-------------------------------------------------------------------------------------
+#define bh_max(a, b)         ((a) > (b) ? (a) : (b))
+#define bh_min(a, b)         ((a) < (b) ? (a) : (b))
+#define bh_clamp(v, a, b)    (bh_min((b), bh_max((a), (v))))
+#define bh_abs(x)            ((x) < 0 ? -(x) : (x))
+#define size_of(x)           (isize) sizeof(x)
+
+static inline u64 log2_dumb(u64 n) {
+    switch (n) {
+    case 1 << 0:  return 0;
+    case 1 << 1:  return 1;
+    case 1 << 2:  return 2;
+    case 1 << 3:  return 3;
+    case 1 << 4:  return 4;
+    case 1 << 5:  return 5;
+    case 1 << 6:  return 6;
+    case 1 << 7:  return 7;
+    case 1 << 8:  return 8;
+    case 1 << 9:  return 9;
+    case 1 << 10: return 10;
+    case 1 << 11: return 11;
+    case 1 << 12: return 12;
+    case 1 << 13: return 13;
+    case 1 << 14: return 14;
+    case 1 << 15: return 15;
+    case 1 << 16: return 16;
+    case 1 << 17: return 17;
+    case 1 << 18: return 18;
+    case 1 << 19: return 19;
+    case 1 << 20: return 20;
+    case 1 << 21: return 21;
+    case 1 << 22: return 22;
+    case 1 << 23: return 23;
+    case 1 << 24: return 24;
+    case 1 << 25: return 25;
+    case 1 << 26: return 26;
+    case 1 << 27: return 27;
+    case 1 << 28: return 28;
+    case 1 << 29: return 29;
+    case 1 << 30: return 30;
+    case 1 << 31: return 31;
+
+    default:   return 0;
+    }
+}
+
+
+
+static inline const char* bh_num_suffix(u64 i) {
+    if (i == 11 || i == 12 || i == 13) return "th";
+
+    switch (i % 10) {
+        case 0: return "th";
+        case 1: return "st";
+        case 2: return "nd";
+        case 3: return "rd";
+        case 4: return "th";
+        case 5: return "th";
+        case 6: return "th";
+        case 7: return "th";
+        case 8: return "th";
+        case 9: return "th";
+
+        default: return "";
+    }
+}
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Conversion functions
+//-------------------------------------------------------------------------------------
+
+// Converts an unsigned integer to the unsigned LEB128 format
+u8* uint_to_uleb128(u64 n, i32* output_length);
+u8* int_to_leb128(i64 n, i32* output_length);
+u8* float_to_ieee754(f32 f, b32 reverse);
+u8* double_to_ieee754(f64 f, b32 reverse);
+
+u64 uleb128_to_uint(u8* bytes, i32 *byte_walker);
+
+
+
+//-------------------------------------------------------------------------------------
+// Helpful macros
+//-------------------------------------------------------------------------------------
+#define bh_offset_of(Type, elem)        ((isize)&(((Type)*) 0)->elem)
+#define bh_align_of(Type)                bh_offset_of(struct { char c; Type member; }, member)
+#define bh_swap(Type, a, b)                do { Type tmp = (a); (a) = (b); (b) = tmp; } while(0)
+
+#define bh_align(x, a) if ((x) % (a) != 0) (x) += (a) - ((x) % (a));
+
+#define bh_pointer_add(ptr, amm)        ((void *)((u8 *) (ptr) + (amm)))
+#define BH_BIT(x)                        (1 << (x))
+#define BH_MASK_SET(var, set, mask)     ((set) ? ((var) |= (mask)) : ((var) &= ~(mask)))
+
+#define fori(var, lo, hi)                 for (i64 var = (lo); var < (hi); var++)
+#define forir(var, hi, lo)                for (i64 var = (hi); var >= (lo); var--)
+#define forll(T, var, start, step)        for (T* var = (start); var != NULL; var = (T *) var->step)
+
+#if defined(BH_DEBUG) && !defined(_BH_WINDOWS)
+    #define DEBUG_HERE                        __asm("int $3")
+#else
+    #define DEBUG_HERE
+#endif
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Custom allocators
+//-------------------------------------------------------------------------------------
+
+typedef enum bh_allocator_actions {
+    bh_allocator_action_alloc,
+    bh_allocator_action_free,
+    bh_allocator_action_resize,
+} bh_allocator_actions;
+
+#define BH_ALLOCATOR_PROC(name) \
+ptr name(ptr data, bh_allocator_actions action, \
+         isize size, isize alignment, \
+         void* prev_memory, \
+         u64 flags)
+
+typedef BH_ALLOCATOR_PROC(bh__allocator_proc); // NOTE: so bh__allocator_proc can be used instead of that type
+
+typedef struct bh_allocator {
+    bh__allocator_proc* proc; // Procedure that can handle bh_allocator_actions
+    ptr                    data; // Pointer to the other data for the allocator
+} bh_allocator;
+
+typedef enum bh_allocator_flags {
+    bh_allocator_flag_clear = 1    // Sets all memory to be 0
+} bh_allocator_flags;
+
+ptr bh_alloc(bh_allocator a, isize size);
+ptr bh_alloc_aligned(bh_allocator a, isize size, isize alignment);
+ptr bh_resize(bh_allocator a, ptr data, isize new_size);
+ptr bh_resize_aligned(bh_allocator a, ptr data, isize new_size, isize alignment);
+void bh_free(bh_allocator a, ptr data);
+
+#define bh_alloc_item(allocator_, T)                (T *) bh_alloc(allocator_, sizeof(T))
+#define bh_alloc_array(allocator_, T, n)            (T *) bh_alloc(allocator_, sizeof(T) * (n))
+
+// NOTE: This should get optimized out since alignment should be a power of two
+#define bh__align(x, alignment)                        ((((x) / alignment) + 1) * alignment)
+
+
+
+
+// HEAP ALLOCATOR
+// Essentially a wrapper for malloc, free and realloc
+bh_allocator bh_heap_allocator(void);
+BH_ALLOCATOR_PROC(bh_heap_allocator_proc);
+
+
+
+
+
+// ARENA ALLOCATOR
+typedef struct bh_arena {
+    bh_allocator backing;
+    ptr first_arena, current_arena;
+    isize size, arena_size; // in bytes
+} bh_arena;
+
+typedef struct bh__arena_internal {
+    ptr next_arena;
+    void* data; // Not actually a pointer, just used for the offset
+} bh__arena_internal;
+
+void bh_arena_init(bh_arena* alloc, bh_allocator backing, isize arena_size);
+void bh_arena_free(bh_arena* alloc);
+bh_allocator bh_arena_allocator(bh_arena* alloc);
+BH_ALLOCATOR_PROC(bh_arena_allocator_proc);
+
+
+
+
+
+
+// SCRATCH ALLOCATOR
+typedef struct bh_scratch {
+    bh_allocator backing;
+    ptr memory, end, curr;
+} bh_scratch;
+
+void bh_scratch_init(bh_scratch* scratch, bh_allocator backing, isize scratch_size);
+void bh_scratch_free(bh_scratch* scratch);
+bh_allocator bh_scratch_allocator(bh_scratch* scratch);
+BH_ALLOCATOR_PROC(bh_scratch_allocator_proc);
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Allocator based string functions
+//-------------------------------------------------------------------------------------
+
+b32 bh_str_starts_with(char* str, char* start);
+b32 bh_str_ends_with(char* str, char* end);
+char* bh_strdup(bh_allocator a, char* str);
+
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Better files
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_FILE
+
+typedef enum bh_file_error {
+    BH_FILE_ERROR_NONE,
+    BH_FILE_ERROR_INVALID,
+    BH_FILE_ERROR_BAD_FD,
+} bh_file_error;
+
+typedef enum bh_file_mode {
+    BH_FILE_MODE_READ = 1 << 0,
+    BH_FILE_MODE_WRITE = 1 << 1,
+    BH_FILE_MODE_APPEND = 1 << 2,
+    BH_FILE_MODE_RW = 1 << 3,
+
+    BH_FILE_MODE_MODES = BH_FILE_MODE_READ | BH_FILE_MODE_WRITE | BH_FILE_MODE_APPEND | BH_FILE_MODE_RW
+} bh_file_mode;
+
+typedef enum bh_file_whence {
+    BH_FILE_WHENCE_BEGIN = SEEK_SET,
+    BH_FILE_WHENCE_CURRENT = SEEK_CUR,
+    BH_FILE_WHENCE_END = SEEK_END,
+} bh_file_whence;
+
+#ifdef _BH_WINDOWS
+    typedef HANDLE bh_file_descriptor;
+#else
+    typedef int bh_file_descriptor;
+#endif
+
+typedef struct bh_file {
+    bh_file_descriptor fd;
+    char const* filename;
+} bh_file;
+
+typedef enum bh_file_standard {
+    BH_FILE_STANDARD_INPUT,
+    BH_FILE_STANDARD_OUTPUT,
+    BH_FILE_STANDARD_ERROR
+} bh_file_standard;
+
+typedef struct bh_file_contents {
+    bh_allocator allocator;
+    const char *filename;
+    isize length;
+    void* data;
+    isize line_count;
+} bh_file_contents;
+
+
+#define BH_FILE_TYPE_DIRECTORY 3
+#define BH_FILE_TYPE_FILE      4
+#define BH_FILE_TYPE_LINK      5
+typedef struct bh_file_stats {
+    isize size;
+    u32 file_type;
+} bh_file_stats;
+
+bh_file_error bh_file_get_standard(bh_file* file, bh_file_standard stand);
+
+bh_file_error bh_file_create(bh_file* file, char const* filename);
+bh_file_error bh_file_open(bh_file* file, char const* filename);
+bh_file_error bh_file_open_mode(bh_file* file, bh_file_mode mode, const char* filename);
+bh_file_error bh_file_new(bh_file* file, bh_file_descriptor fd, const char* filename);
+b32 bh_file_read_at(bh_file* file, i64 offset, void* buffer, isize buff_size, isize* bytes_read);
+b32 bh_file_write_at(bh_file* file, i64 offset, void const* buffer, isize buff_size, isize* bytes_wrote);
+i64 bh_file_seek(bh_file* file, i64 offset, bh_file_whence whence);
+i64 bh_file_seek_to(bh_file* file, i64 offset);
+i64 bh_file_seek_to_end(bh_file* file);
+i64 bh_file_skip(bh_file* file, i64 bytes);
+i64 bh_file_tell(bh_file* file);
+bh_file_error bh_file_close(bh_file* file);
+i32 bh_file_read(bh_file* file, void* buffer, isize buff_size);
+i32 bh_file_write(bh_file* file, void* buffer, isize buff_size);
+void bh_file_flush(bh_file* file);
+i64 bh_file_size(bh_file* file);
+b32 bh_file_stat(char const* filename, bh_file_stats* stat);
+b32 bh_file_exists(char const* filename);
+b32 bh_file_remove(char const* filename);
+char* bh_path_get_full_name(char const* filename, bh_allocator a);
+char* bh_path_get_parent(char const* filename, bh_allocator a);
+char* bh_path_convert_separators(char* path);
+
+// This function returns a volatile pointer. Do not store it without copying!
+// `included_folders` is bh_arr(const char *).
+char* bh_lookup_file(char* filename, char* relative_to, char *suffix, b32 add_suffix, const char ** included_folders, b32 search_included_folders);
+
+#define bh_file_read_contents(allocator_, x) _Generic((x), \
+    bh_file*: bh_file_read_contents_bh_file, \
+    const char*: bh_file_read_contents_direct, \
+    char*: bh_file_read_contents_direct)((allocator_), x)
+
+bh_file_contents bh_file_read_contents_bh_file(bh_allocator alloc, bh_file* file);
+bh_file_contents bh_file_read_contents_direct(bh_allocator alloc, const char* filename);
+i32 bh_file_contents_free(bh_file_contents* contents);
+
+
+#ifdef _BH_WINDOWS
+    typedef struct Windows_Directory_Opened {
+        HANDLE hndl;
+        WIN32_FIND_DATAA found_file;
+    } Windows_Directory_Opened;
+
+    typedef Windows_Directory_Opened *bh_dir;
+#else
+    typedef DIR *bh_dir;
+#endif
+
+typedef enum bh_dirent_type {
+    BH_DIRENT_UNKNOWN,
+    BH_DIRENT_BLOCK,
+    BH_DIRENT_CHAR,
+    BH_DIRENT_DIRECTORY,
+    BH_DIRENT_FILE,
+    BH_DIRENT_SYMLINK,
+    BH_DIRENT_OTHER,
+} bh_dirent_type;
+
+typedef struct bh_dirent {
+    bh_dirent_type type;
+    u32 id;
+    u32 name_length;
+    char name[256];
+} bh_dirent;
+
+bh_dir bh_dir_open(char* path);
+b32    bh_dir_read(bh_dir dir, bh_dirent* out);
+void   bh_dir_close(bh_dir dir);
+
+#endif
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Alternate printing
+//-------------------------------------------------------------------------------------
+// Barebones implementation of printf. Does not support all format options
+// Currently supports:
+//         %c - chars
+//        %_(u)d - ints where _ is:
+//            nothing - decimal
+//            o - octal
+//            x - hexadecimal
+//        %_(u)l - longs where _ is:
+//            nothing - decimal
+//            o - octal
+//            x - hexadecimal
+//        %f - floating points
+//        %s - null terminated strings
+//        %p - pointers
+//        %% - literal %
+
+typedef struct bh__print_format {
+    u32 base;
+} bh__print_format;
+
+isize bh_printf(char const *fmt, ...);
+isize bh_printf_va(char const *fmt, va_list va);
+isize bh_printf_err(char const *fmt, ...);
+isize bh_printf_err_va(char const *fmt, va_list va);
+isize bh_fprintf(bh_file* f, char const *fmt, ...);
+isize bh_fprintf_va(bh_file* f, char const *fmt, va_list va);
+char* bh_bprintf(char const *fmt, ...);
+char* bh_bprintf_va(char const *fmt, va_list va);
+char* bh_aprintf(bh_allocator alloc, const char* fmt, ...);
+char* bh_aprintf_va(bh_allocator alloc, const char* fmt, va_list va);
+isize bh_snprintf(char *str, isize n, char const *fmt, ...);
+isize bh_snprintf_va(char *str, isize n, char const *fmt, va_list va);
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Flexible buffer
+//-------------------------------------------------------------------------------------
+
+typedef struct bh_buffer {
+    bh_allocator allocator;
+    i32 length, capacity;
+    u8* data;
+} bh_buffer;
+
+#ifndef BH_BUFFER_GROW_FORMULA
+#define BH_BUFFER_GROW_FORMULA(x)            ((x) > 0 ? ((x) << 1) : 16)
+#endif
+
+void bh_buffer_init(bh_buffer* buffer, bh_allocator alloc, i32 length);
+void bh_buffer_free(bh_buffer* buffer);
+void bh_buffer_clear(bh_buffer* buffer);
+void bh_buffer_grow(bh_buffer* buffer, i32 length);
+void bh_buffer_append(bh_buffer* buffer, const void * data, i32 length);
+void bh_buffer_concat(bh_buffer* buffer, bh_buffer other);
+void bh_buffer_write_byte(bh_buffer* buffer, u8 byte);
+void bh_buffer_write_u32(bh_buffer* buffer, u32 i);
+void bh_buffer_write_u64(bh_buffer* buffer, u64 i);
+void bh_buffer_align(bh_buffer* buffer, u32 alignment);
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Better dynamically-sized arrays
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_ARRAY
+
+typedef struct bh__arr {
+    bh_allocator allocator;
+    i32 length, capacity;
+} bh__arr;
+
+#ifndef BH_ARR_GROW_FORMULA
+#define BH_ARR_GROW_FORMULA(x)       ((x) > 0 ? ((x) << 1) : 4)
+#endif
+
+#define bh_arr(T)                    T*
+#define bh__arrhead(arr)             (((bh__arr *)(arr)) - 1)
+
+#define bh_arr_allocator(arr)        (arr ? bh__arrhead(arr)->allocator : bh_heap_allocator())
+#define bh_arr_length(arr)           (arr ? bh__arrhead(arr)->length : 0)
+#define bh_arr_capacity(arr)         (arr ? bh__arrhead(arr)->capacity : 0)
+#define bh_arr_size(arr)             (arr ? bh__arrhead(arr)->capacity * sizeof(*(arr)) : 0)
+#define bh_arr_valid(arr, i)         (arr ? (i32)(i) < bh__arrhead(arr)->length : 0)
+
+#define bh_arr_pop(arr)              ((arr)[--bh__arrhead(arr)->length])
+#define bh_arr_last(arr)             ((arr)[bh__arrhead(arr)->length - 1])
+#define bh_arr_end(arr, i)           ((i) >= &(arr)[bh_arr_length(arr)])
+#define bh_arr_start(arr, i)         ((i) < (arr))
+
+#define bh_arr_new(allocator_, arr, cap)    (bh__arr_grow((allocator_), (void**) &(arr), sizeof(*(arr)), cap))
+#define bh_arr_free(arr)                    (bh__arr_free((void**) &(arr)))
+#define bh_arr_copy(allocator_, arr)        (bh__arr_copy((allocator_), (arr), sizeof(*(arr))))
+
+#define bh_arr_grow(arr, cap)          (bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), cap))
+#define bh_arr_shrink(arr, cap)        (bh__arr_shrink((void **) &(arr), sizeof(*(arr)), cap))
+#define bh_arr_set_length(arr, n)      (bh__arrhead(arr)->length = n)
+
+#define bh_arr_insertn(arr, i, n)      (bh__arr_insertn((void **) &(arr), sizeof(*(arr)), i, n))
+
+#define bh_arr_insert_end(arr, n)      ( \
+    bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), bh_arr_length(arr) + n), \
+    bh__arrhead(arr)->length += n)
+
+#define bh_arr_push(arr, value)       ( \
+    bh_arr_length(arr) + 1 > bh_arr_capacity(arr) ? bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), bh_arr_length(arr) + 1) : 0, \
+    arr[bh__arrhead(arr)->length++] = value)
+
+#define bh_arr_set_at(arr, n, value) ( \
+    bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), (n) + 1), \
+    bh_arr_set_length((arr), bh_max(bh_arr_length(arr), (i32) (n) + 1)), \
+    arr[n] = value)
+
+#define bh_arr_is_empty(arr)          (arr ? bh__arrhead(arr)->length == 0 : 1)
+#define bh_arr_clear(arr)             (arr ? (bh__arrhead(arr)->length = 0) : 0)
+
+#define bh_arr_deleten(arr, i, n)     (bh__arr_deleten((void **) &(arr), sizeof(*(arr)), i, n))
+#define bh_arr_fastdelete(arr, i)     (arr[i] = arr[--bh__arrhead(arr)->length])
+
+#define bh_arr_each(T, var, arr)      for (T* var = (arr); !bh_arr_end((arr), var); var++)
+#define bh_arr_rev_each(T, var, arr)  for (T* var = &bh_arr_last((arr)); !bh_arr_start((arr), var); var--)
+
+#define bh_arr_zero(arr) memset(arr, 0, bh_arr_length(arr) * sizeof(*(arr)));
+
+b32 bh__arr_grow(bh_allocator alloc, void** arr, i32 elemsize, i32 cap);
+b32 bh__arr_shrink(void** arr, i32 elemsize, i32 cap);
+b32 bh__arr_free(void **arr);
+void* bh__arr_copy(bh_allocator alloc, void *arr, i32 elemsize);
+void bh__arr_insertn(void **arr, i32 elemsize, i32 index, i32 numelems);
+void bh__arr_deleten(void **arr, i32 elemsize, i32 index, i32 numelems);
+
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// STRING HASH TABLE FUNCTIONS
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_TABLE
+
+#ifdef BH_DEFINE
+u64 bh__table_hash_function(const char* str, i32 len, i32 mod) {
+    u64 hash = 5381;
+    i32 c, l = 0;
+    if (len == 0) len = ((u32) 1 << 31) - 1;
+
+    while ((c = *str++) && l++ < len) {
+        hash = (hash << 5) + hash + c;
+    }
+
+    return hash % mod;
+}
+#endif
+
+typedef struct bh_table_iterator {
+    ptr *tab, *endtab;
+    i32 elemsize, arrlen;
+    ptr entry;
+} bh_table_iterator;
+
+typedef struct bh__table {
+    bh_allocator allocator;
+    u64 table_size; // NOTE: u64 since padding will make it 8-bytes no matter what
+    ptr arrs[];
+} bh__table;
+
+#define bh_table(T)        T*
+
+#ifdef BH_TABLE_SIZE_SAFE
+    #define bh_table_init(allocator_, tab, hs)    bh__table_init(allocator_, (bh__table **)&(tab), hs)
+    #define bh_table_free(tab)                    bh__table_free((bh__table **)&(tab))
+    #define bh_table_put(T, tab, key, value)      (assert(sizeof(T) == sizeof(*(tab))), (*((T *) bh__table_put((bh__table *) tab, sizeof(T), key)) = (T) value))
+    #define bh_table_has(T, tab, key)             (assert(sizeof(T) == sizeof(*(tab))), (bh__table_has((bh__table *) tab, sizeof(T), key)))
+    #define bh_table_get(T, tab, key)             (assert(sizeof(T) == sizeof(*(tab))), (*((T *) bh__table_get((bh__table *) tab, sizeof(T), key))))
+    #define bh_table_delete(T, tab, key)          (assert(sizeof(T) == sizeof(*(tab))), bh__table_delete((bh__table *) tab, sizeof(T), key))
+    #define bh_table_clear(tab)                   (bh__table_clear((bh__table *) tab))
+
+    #define bh_table_iter_setup(T, tab)           (assert(sizeof(T) == sizeof(*(tab))), bh__table_iter_setup((bh__table *) tab, sizeof(T)))
+    #define bh_table_iter_key(it)                 ((char *)(bh_pointer_add(it.entry, it.elemsize + sizeof(u16))))
+    #define bh_table_iter_value(T, it)            (*(T *)it.entry)
+#else
+    #define bh_table_init(allocator_, tab, hs)    bh__table_init(allocator_, (bh__table **)&(tab), hs)
+    #define bh_table_free(tab)                    bh__table_free((bh__table **)&(tab))
+    #define bh_table_put(T, tab, key, value)      (*((T *) bh__table_put((bh__table *) tab, sizeof(T), key)) = value)
+    #define bh_table_has(T, tab, key)             (bh__table_has((bh__table *) tab, sizeof(T), key))
+    #define bh_table_get(T, tab, key)             (*((T *) bh__table_get((bh__table *) tab, sizeof(T), key)))
+    #define bh_table_delete(T, tab, key)          (bh__table_delete((bh__table *) tab, sizeof(T), key))
+    #define bh_table_clear(tab)                   (bh__table_clear((bh__table *) tab))
+
+    #define bh_table_iter_setup(T, tab)           (bh__table_iter_setup((bh__table *) tab, sizeof(T)))
+    #define bh_table_iter_key(it)                 ((char *)(bh_pointer_add(it.entry, it.elemsize + sizeof(u16))))
+    #define bh_table_iter_value(T, it)            (*(T *)it.entry)
+#endif
+
+#define bh_table_each_start(T, table) { \
+    bh_table_iterator it = bh_table_iter_setup(T, (table)); \
+    while (bh_table_iter_next(&it)) { \
+        const char* key = bh_table_iter_key(it); \
+        T value = bh_table_iter_value(T, it);
+#define bh_table_each_end            } }
+
+b32 bh__table_init(bh_allocator allocator, bh__table **table, i32 table_size);
+b32 bh__table_free(bh__table **table);
+ptr bh__table_put(bh__table *table, i32 elemsize, char *key);
+b32 bh__table_has(bh__table *table, i32 elemsize, char *key);
+ptr bh__table_get(bh__table *table, i32 elemsize, char *key);
+void bh__table_delete(bh__table *table, i32 elemsize, char *key);
+void bh__table_clear(bh__table *table);
+bh_table_iterator bh__table_iter_setup(bh__table *table, i32 elemsize);
+b32 bh_table_iter_next(bh_table_iterator* it);
+
+#endif
+// Using stb_ds for tables now because they are better in every single way.
+#define Table(T) struct { char *key; T value; } *
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------
+// IMAP (integer to integer map)
+//-------------------------------------------------------------------------------
+#ifndef BH_NO_IMAP
+
+typedef u64 bh_imap_entry_t;
+
+typedef struct bh__imap_lookup_result {
+    i64 hash_index;
+    i64 entry_prev;
+    i64 entry_index;
+} bh__imap_lookup_result;
+
+typedef struct bh__imap_entry {
+    bh_imap_entry_t key, value;
+    i64 next;
+} bh__imap_entry;
+
+typedef struct bh_imap {
+    bh_allocator allocator;
+
+    bh_arr(i64)            hashes;
+    bh_arr(bh__imap_entry) entries;
+} bh_imap;
+
+
+void bh_imap_init(bh_imap* imap, bh_allocator alloc, i32 hash_count);
+void bh_imap_free(bh_imap* imap);
+void bh_imap_put(bh_imap* imap, bh_imap_entry_t key, bh_imap_entry_t value);
+b32 bh_imap_has(bh_imap* imap, bh_imap_entry_t key);
+bh_imap_entry_t bh_imap_get(bh_imap* imap, bh_imap_entry_t key);
+void bh_imap_delete(bh_imap* imap, bh_imap_entry_t key);
+void bh_imap_clear(bh_imap* imap);
+
+#ifdef BH_DEFINE
+#endif // BH_DEFINE
+
+
+#endif
+
+
+
+
+
+
+
+
+
+
+
+// MANAGED HEAP ALLOCATOR
+typedef struct bh_managed_heap {
+    bh_imap ptrs;
+} bh_managed_heap;
+
+void bh_managed_heap_init(bh_managed_heap* mh);
+void bh_managed_heap_free(bh_managed_heap* mh);
+bh_allocator bh_managed_heap_allocator(bh_managed_heap* mh);
+BH_ALLOCATOR_PROC(bh_managed_heap_allocator_proc);
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------
+// OTHER COMMON DATA STRUCTURES
+//-------------------------------------------------------------------------------
+#ifndef BH_NO_DATASTRUCTURES
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#endif // BH_NO_DATASTRUCTURES
+
+
+
+
+
+//------------------------------------------------------------------------------
+// TIME / DURATION
+//------------------------------------------------------------------------------
+u64 bh_time_curr();
+u64 bh_time_duration(u64 old);
+
+
+
+
+
+
+
+
+
+
+
+#ifdef BH_DEFINE
+
+#undef BH_DEFINE
+//-------------------------------------------------------------------------------------
+// IMPLEMENTATIONS
+//-------------------------------------------------------------------------------------
+
+//-------------------------------------------------------------------------------------
+// CHAR FUNCTIONS
+//-------------------------------------------------------------------------------------
+
+b32 char_is_alpha(const char a) {
+    return ('a' <= a && a <= 'z') || ('A' <= a && a <= 'Z');
+}
+
+char charset_contains(const char* charset, char ch) {
+    while (*charset) {
+        if (*charset == ch) return ch;
+        charset++;
+    }
+
+    return 0;
+}
+
+b32 char_is_num(const char a) {
+    return ('0' <= a && a <= '9');
+}
+
+b32 char_is_alphanum(const char a) {
+    return char_is_alpha(a) || char_is_num(a);
+}
+
+b32 char_is_whitespace(const char a) {
+    return charset_contains(" \t\r\n", a);
+}
+
+b32 char_in_range(const char lo, const char hi, const char a) {
+    return lo <= a && a <= hi;
+}
+
+i64 chars_match(char* ptr1, char* ptr2) {
+    i64 len = 0;
+    while (*ptr2 != '\0' && *ptr1 == *ptr2) ptr1++, ptr2++, len++;
+    return *ptr2 == '\0' ? len : 0;
+}
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// CUSTOM ALLOCATORS IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+ptr bh_alloc(bh_allocator a, isize size) {
+    return bh_alloc_aligned(a, size, 16);
+}
+
+ptr bh_alloc_aligned(bh_allocator a, isize size, isize alignment) {
+    return a.proc(a.data, bh_allocator_action_alloc, size, alignment, NULL,  0);
+}
+
+ptr bh_resize(bh_allocator a, ptr data, isize new_size) {
+    return bh_resize_aligned(a, data, new_size, 16);
+}
+
+ptr bh_resize_aligned(bh_allocator a, ptr data, isize new_size, isize alignment) {
+    return a.proc(a.data, bh_allocator_action_resize, new_size, alignment, data, 0);
+}
+
+void bh_free(bh_allocator a, ptr data) {
+    if (data != NULL) a.proc(a.data, bh_allocator_action_free, 0, 0, data, 0);
+}
+
+
+
+// HEAP ALLOCATOR IMPLEMENTATION
+bh_allocator bh_heap_allocator(void) {
+    return (bh_allocator) {
+        .proc = bh_heap_allocator_proc,
+        .data = NULL
+    };
+}
+
+BH_ALLOCATOR_PROC(bh_heap_allocator_proc) {
+    ptr retval = NULL;
+
+    switch (action) {
+    case bh_allocator_action_alloc: {
+#if defined(_BH_WINDOWS)
+        retval = _aligned_malloc(size, alignment);
+#elif defined(_BH_LINUX)
+        i32 success = posix_memalign(&retval, alignment, size);
+#endif
+        if (flags & bh_allocator_flag_clear && retval != NULL) {
+            memset(retval, 0, size);
+        }
+    } break;
+
+    case bh_allocator_action_resize: {
+        // TODO: Maybe replace with better custom function
+#if defined(_BH_WINDOWS)
+        retval = _aligned_realloc(prev_memory, size, alignment);
+#elif defined(_BH_LINUX)
+        retval = realloc(prev_memory, size);
+#endif
+    } break;
+
+    case bh_allocator_action_free: {
+#if defined(_BH_WINDOWS)
+        _aligned_free(prev_memory);
+#elif defined(_BH_LINUX)
+        free(prev_memory);
+#endif
+    } break;
+    }
+
+    return retval;
+}
+
+
+
+
+
+
+// MANAGED HEAP ALLOCATOR IMPLEMENTATION
+void bh_managed_heap_init(bh_managed_heap* mh) {
+    bh_imap_init(&mh->ptrs, bh_heap_allocator(), 512);
+}
+
+void bh_managed_heap_free(bh_managed_heap* mh) {
+    bh_arr_each(bh__imap_entry, p, mh->ptrs.entries) {
+#if defined(_BH_WINDOWS)
+        _aligned_free((void *) p->key);
+#elif defined(_BH_LINUX)
+        free((void *) p->key);
+#endif
+    }
+
+    bh_imap_free(&mh->ptrs);
+}
+
+bh_allocator bh_managed_heap_allocator(bh_managed_heap* mh) {
+    return (bh_allocator) {
+        .proc = bh_managed_heap_allocator_proc,
+        .data = mh
+    };
+}
+
+BH_ALLOCATOR_PROC(bh_managed_heap_allocator_proc) {
+    bh_managed_heap* mh = (bh_managed_heap *) data;
+    ptr retval = NULL;
+
+    switch (action) {
+    case bh_allocator_action_alloc: {
+#if defined(_BH_WINDOWS)
+        retval = _aligned_malloc(size, alignment);
+#elif defined(_BH_LINUX)
+        i32 success = posix_memalign(&retval, alignment, size);
+#endif
+
+        if (flags & bh_allocator_flag_clear && retval != NULL) {
+            memset(retval, 0, size);
+        }
+
+        if (retval != NULL)
+            bh_imap_put(&mh->ptrs, (u64) retval, 1);
+    } break;
+
+    case bh_allocator_action_resize: {
+        bh_imap_delete(&mh->ptrs, (u64) prev_memory);
+#if defined(_BH_WINDOWS)
+        retval = _aligned_realloc(prev_memory, size, alignment);
+#elif defined(_BH_LINUX)
+        retval = realloc(prev_memory, size);
+#endif
+        bh_imap_put(&mh->ptrs, (u64) retval, 1);
+    } break;
+
+    case bh_allocator_action_free: {
+        bh_imap_delete(&mh->ptrs, (u64) prev_memory);
+#if defined(_BH_WINDOWS)
+        _aligned_free(prev_memory);
+#elif defined(_BH_LINUX)
+        free(prev_memory);
+#endif
+    } break;
+    }
+
+    return retval;
+}
+
+
+
+
+
+
+
+
+// ARENA ALLOCATOR IMPLEMENTATION
+void bh_arena_init(bh_arena* alloc, bh_allocator backing, isize arena_size) {
+    arena_size = bh_max(arena_size, size_of(ptr));
+    ptr data = bh_alloc(backing, arena_size);
+
+    alloc->backing = backing;
+    alloc->arena_size = arena_size;
+    alloc->size = sizeof(ptr);
+    alloc->first_arena = data;
+    alloc->current_arena = data;
+
+    ((bh__arena_internal *)(alloc->first_arena))->next_arena = NULL;
+}
+
+void bh_arena_free(bh_arena* alloc) {
+    bh__arena_internal *walker = (bh__arena_internal *) alloc->first_arena;
+    bh__arena_internal *trailer = walker;
+    while (walker != NULL) {
+        walker = walker->next_arena;
+        bh_free(alloc->backing, trailer);
+        trailer = walker;
+    }
+
+    alloc->first_arena = NULL;
+    alloc->current_arena = NULL;
+    alloc->arena_size = 0;
+    alloc->size = 0;
+}
+
+bh_allocator bh_arena_allocator(bh_arena* alloc) {
+    return (bh_allocator) {
+        .proc = bh_arena_allocator_proc,
+        .data = alloc,
+    };
+}
+
+BH_ALLOCATOR_PROC(bh_arena_allocator_proc) {
+    bh_arena* alloc_arena = (bh_arena*) data;
+
+    ptr retval = NULL;
+
+    switch (action) {
+    case bh_allocator_action_alloc: {
+        bh_align(size, alignment);
+        bh_align(alloc_arena->size, alignment);
+
+        if (size > alloc_arena->arena_size - size_of(ptr)) {
+            // Size too large for the arena
+            return NULL;
+        }
+
+        if (alloc_arena->size + size >= alloc_arena->arena_size) {
+            bh__arena_internal* new_arena = (bh__arena_internal *) bh_alloc(alloc_arena->backing, alloc_arena->arena_size);
+
+            if (new_arena == NULL) {
+                bh_printf_err("Arena Allocator: couldn't allocate new arena");
+                return NULL;
+            }
+
+            new_arena->next_arena = NULL;
+            ((bh__arena_internal *)(alloc_arena->current_arena))->next_arena = new_arena;
+            alloc_arena->current_arena = new_arena;
+            alloc_arena->size = sizeof(ptr);
+        }
+
+        retval = bh_pointer_add(alloc_arena->current_arena, alloc_arena->size);
+        alloc_arena->size += size;
+    } break;
+
+    case bh_allocator_action_resize: {
+        // Do nothing since this is a fixed allocator
+    } break;
+
+    case bh_allocator_action_free: {
+        // Do nothing since this allocator isn't made for freeing memory
+    } break;
+    }
+
+    return retval;
+}
+
+
+
+
+// SCRATCH ALLOCATOR IMPLEMENTATION
+void bh_scratch_init(bh_scratch* scratch, bh_allocator backing, isize scratch_size) {
+    ptr memory = bh_alloc(backing, scratch_size);
+
+    scratch->backing = backing;
+    scratch->memory = memory;
+    scratch->curr = memory;
+    scratch->end = bh_pointer_add(memory, scratch_size);
+}
+
+void bh_scratch_free(bh_scratch* scratch) {
+    bh_free(scratch->backing, scratch->memory);
+
+    scratch->memory = NULL;
+    scratch->curr = NULL;
+    scratch->end = NULL;
+}
+
+bh_allocator bh_scratch_allocator(bh_scratch* scratch) {
+    return (bh_allocator) {
+        .proc = bh_scratch_allocator_proc,
+        .data = scratch,
+    };
+}
+
+BH_ALLOCATOR_PROC(bh_scratch_allocator_proc) {
+    bh_scratch* scratch = (bh_scratch*) data;
+    ptr retval = NULL;
+
+    switch (action) {
+    case bh_allocator_action_alloc: {
+        if (size > ((u8 *) scratch->end) - ((u8 *) scratch->memory)) {
+            return NULL;
+        }
+
+        retval = scratch->curr;
+        scratch->curr = bh_pointer_add(scratch->curr, size);
+
+        if (scratch->curr >= scratch->end) {
+            scratch->curr = scratch->memory;
+            retval = scratch->curr;
+        }
+    } break;
+
+    case bh_allocator_action_free: break;
+
+    case bh_allocator_action_resize: {
+        if (size > ((u8 *) scratch->end) - ((u8 *) scratch->memory)) {
+            return NULL;
+        }
+
+        retval = scratch->curr;
+        scratch->curr = bh_pointer_add(scratch->curr, size);
+
+        if (scratch->curr >= scratch->end) {
+            scratch->curr = scratch->memory;
+            retval = scratch->curr;
+        }
+
+        // HACK!!!!!: Using size instead of some kind of "old size"
+        memcpy(retval, prev_memory, size);
+    } break;
+    }
+
+    return retval;
+}
+
+
+
+
+//-------------------------------------------------------------------------------------
+// CONVERSION FUNCTIONS IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+u8* uint_to_uleb128(u64 n, i32* output_length) {
+    static u8 buffer[16];
+
+    *output_length = 0;
+    u8* output = buffer;
+    u8 byte;
+    do {
+        byte = n & 0x7f;
+        n >>= 7;
+        if (n != 0) byte |= (1 << 7);
+        *output++ = byte;
+        (*output_length)++;
+    } while (n != 0);
+
+    return buffer;
+}
+
+
+// Converts a signed integer to the signed LEB128 format
+u8* int_to_leb128(i64 n, i32* output_length) {
+    static u8 buffer[16];
+
+    *output_length = 0;
+    u8* output = buffer;
+    b32 more = 1;
+
+    i32 size = 64;
+
+    u8 byte;
+    do {
+        byte = n & 0x7f;
+        n >>= 7;
+
+        more = !(((n == 0) && (byte & 0x40) == 0) || ((n == -1) && (byte & 0x40) != 0));
+        if (more)
+            byte |= 0x80;
+        *output++ = byte;
+        (*output_length)++;
+    } while (more);
+
+    return buffer;
+}
+
+// NOTE: This assumes the underlying implementation of float on the host
+// system is already IEEE-754. This is safe to assume in most cases.
+u8* float_to_ieee754(f32 f, b32 reverse) {
+    static u8 buffer[4];
+
+    u8* fmem = (u8*) &f;
+    if (reverse) {
+        buffer[0] = fmem[3];
+        buffer[1] = fmem[2];
+        buffer[2] = fmem[1];
+        buffer[3] = fmem[0];
+    } else {
+        buffer[0] = fmem[0];
+        buffer[1] = fmem[1];
+        buffer[2] = fmem[2];
+        buffer[3] = fmem[3];
+    }
+
+    return buffer;
+}
+
+u8* double_to_ieee754(f64 f, b32 reverse) {
+    static u8 buffer[8];
+
+    u8* fmem = (u8*) &f;
+    if (reverse) {
+        buffer[0] = fmem[7];
+        buffer[1] = fmem[6];
+        buffer[2] = fmem[5];
+        buffer[3] = fmem[4];
+        buffer[4] = fmem[3];
+        buffer[5] = fmem[2];
+        buffer[6] = fmem[1];
+        buffer[7] = fmem[0];
+    } else {
+        buffer[0] = fmem[0];
+        buffer[1] = fmem[1];
+        buffer[2] = fmem[2];
+        buffer[3] = fmem[3];
+        buffer[4] = fmem[4];
+        buffer[5] = fmem[5];
+        buffer[6] = fmem[6];
+        buffer[7] = fmem[7];
+    }
+
+    return buffer;
+}
+
+u64 uleb128_to_uint(u8* bytes, i32 *byte_count) {
+    u64 res = 0;
+    u64 shift = 0;
+
+    while (1) {
+        u8 byte = bytes[(*byte_count)++];
+        res |= (byte & 0x7f) << shift;
+        if ((byte & 0x80) == 0) break;
+        shift += 7;
+    }
+    return res;
+}
+
+
+
+//-------------------------------------------------------------------------------------
+// STRING IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+b32 bh_str_starts_with(char* str, char* start) {
+    char* s = str;
+    char* p = start;
+
+    while (*s != '\0' && *p != '\0' && *s == *p) s++, p++;
+
+    return *p == '\0';
+}
+
+b32 bh_str_ends_with(char* str, char* end) {
+    i32 slen = strlen(str);
+    i32 elen = strlen(end);
+
+    char* s = str + slen - 1;
+    char* e = end + elen - 1;
+
+    while (*e == *s && e != end && s != str) e--, s--;
+
+    return *e == *s;
+}
+
+char* bh_strdup(bh_allocator a, char* str) {
+    u32 len = strlen(str);
+    char* buf = bh_alloc(a, len + 1);
+
+    char* t = buf;
+    while ((*t++ = *str++));
+    return buf;
+}
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// FILE IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_FILE
+
+static b32 bh__file_seek_wrapper(bh_file_descriptor fd, i64 offset, bh_file_whence whence, i64* new_offset);
+
+bh_file_error bh_file_get_standard(bh_file* file, bh_file_standard stand) {
+    const char* filename = NULL;
+
+#if defined(_BH_WINDOWS)
+    bh_file_descriptor sd_fd;
+
+    switch (stand) {
+    case BH_FILE_STANDARD_INPUT:
+        sd_fd = GetStdHandle(STD_INPUT_HANDLE);
+        filename = "stdin";
+        break;
+    case BH_FILE_STANDARD_OUTPUT:
+        sd_fd = GetStdHandle(STD_OUTPUT_HANDLE);
+        filename = "stdout";
+        break;
+    case BH_FILE_STANDARD_ERROR:
+        sd_fd = GetStdHandle(STD_ERROR_HANDLE);
+        filename = "stderr";
+        break;
+    default:
+        return BH_FILE_ERROR_BAD_FD;
+    }
+    file->fd = sd_fd;
+
+#elif defined(_BH_LINUX)
+    i32 sd_fd = -1;
+
+    switch (stand) {
+    case BH_FILE_STANDARD_INPUT:
+        sd_fd = STDIN_FILENO;
+        filename = "stdin"; // These are constants in the data section so everything should be okay
+        break;
+    case BH_FILE_STANDARD_OUTPUT:
+        sd_fd = STDOUT_FILENO;
+        filename = "stdout";
+        break;
+    case BH_FILE_STANDARD_ERROR:
+        sd_fd = STDERR_FILENO;
+        filename = "stderr";
+        break;
+    default:
+        return BH_FILE_ERROR_BAD_FD;
+    }
+
+    file->fd = sd_fd;
+#endif
+
+
+    file->filename = filename;
+    return BH_FILE_ERROR_NONE;
+}
+
+bh_file_error bh_file_create(bh_file* file, const char* filename) {
+    // Need to do this to avoid compiler complaining about types
+    bh_file_mode write_rw = (bh_file_mode) (BH_FILE_MODE_WRITE | BH_FILE_MODE_RW);
+    return bh_file_open_mode(file, write_rw, filename);
+}
+
+bh_file_error bh_file_open(bh_file* file, const char* filename) {
+    return bh_file_open_mode(file, BH_FILE_MODE_READ, filename);
+}
+
+bh_file_error bh_file_open_mode(bh_file* file, bh_file_mode mode, const char* filename) {
+#if defined(_BH_WINDOWS)
+    DWORD desired_access;
+    DWORD creation_disposition;
+
+    switch (mode & BH_FILE_MODE_MODES) {
+        case BH_FILE_MODE_READ:
+            desired_access = GENERIC_READ;
+            creation_disposition = OPEN_EXISTING;
+            break;
+        case BH_FILE_MODE_WRITE:
+            desired_access = GENERIC_WRITE;
+            creation_disposition = CREATE_ALWAYS;
+            break;
+        case BH_FILE_MODE_APPEND:
+            desired_access = GENERIC_WRITE;
+            creation_disposition = OPEN_ALWAYS;
+            break;
+        case BH_FILE_MODE_READ | BH_FILE_MODE_RW:
+            desired_access = GENERIC_READ | GENERIC_WRITE;
+            creation_disposition = OPEN_EXISTING;
+            break;
+        case BH_FILE_MODE_WRITE | BH_FILE_MODE_RW:
+            desired_access = GENERIC_READ | GENERIC_WRITE;
+            creation_disposition = CREATE_ALWAYS;
+            break;
+        case BH_FILE_MODE_APPEND | BH_FILE_MODE_RW:
+            desired_access = GENERIC_READ | GENERIC_WRITE;
+            creation_disposition = OPEN_ALWAYS;
+            break;
+        default:
+            return BH_FILE_ERROR_INVALID;
+    }
+
+
+    file->fd = CreateFileA(filename,
+                    desired_access,
+                    FILE_SHARE_READ,
+                    NULL,
+                    creation_disposition,
+                    FILE_ATTRIBUTE_NORMAL,
+                    NULL);
+
+    if (file->fd == INVALID_HANDLE_VALUE) {
+        return BH_FILE_ERROR_INVALID;
+    }
+
+    file->filename = filename;
+    return BH_FILE_ERROR_NONE;
+
+#elif defined(_BH_LINUX)
+    i32 os_mode = 0;
+
+    switch (mode & BH_FILE_MODE_MODES) {
+    case BH_FILE_MODE_READ:                       os_mode = O_RDONLY; break;
+    case BH_FILE_MODE_WRITE:                      os_mode = O_WRONLY | O_CREAT | O_TRUNC; break;
+    case BH_FILE_MODE_APPEND:                     os_mode = O_WRONLY | O_APPEND | O_CREAT; break;
+    case BH_FILE_MODE_READ   | BH_FILE_MODE_RW:   os_mode = O_RDWR; break;
+    case BH_FILE_MODE_WRITE  | BH_FILE_MODE_RW:   os_mode = O_RDWR | O_CREAT | O_TRUNC; break;
+    case BH_FILE_MODE_APPEND | BH_FILE_MODE_RW:   os_mode = O_RDWR | O_APPEND | O_CREAT; break;
+    //default: // TODO Handle errors
+    }
+
+    file->fd = open(filename, os_mode,
+        S_IRUSR | S_IWUSR | S_IROTH | S_IWOTH | S_IRGRP | S_IWGRP //+rw-rw-rw-
+    );
+    if (file->fd < 0) {
+        return BH_FILE_ERROR_INVALID;
+    }
+
+    // TODO: Set this using some allocator
+    file->filename = filename;
+
+    return BH_FILE_ERROR_NONE;
+#endif
+}
+
+bh_file_error bh_file_new(bh_file* file, bh_file_descriptor fd, const char* filename) {
+    file->filename = filename; // This may be unsafe
+    file->fd = fd;
+    return BH_FILE_ERROR_NONE;
+}
+
+b32 bh_file_read_at(bh_file* file, i64 offset, void* buffer, isize buff_size, isize* bytes_read) {
+#if defined(_BH_WINDOWS)
+    bh_file_seek_to(file, offset);
+    BOOL res = ReadFile(file->fd, buffer, buff_size, (i32 *) bytes_read, NULL);
+    if (res) return 1;
+    else     return 0;
+
+#elif defined(_BH_LINUX)
+    if (file->fd == 0) {
+        isize res = read(file->fd, buffer, buff_size);
+        if (res < 0) return 0;
+        if (bytes_read) *bytes_read = res;
+        return 1;
+    }
+
+    isize res = pread(file->fd, buffer, buff_size, offset);
+    if (res < 0) return 0;
+    if (bytes_read) *bytes_read = res;
+    return 1;
+#endif
+}
+
+b32 bh_file_write_at(bh_file* file, i64 offset, void const* buffer, isize buff_size, isize* bytes_wrote) {
+    isize res;
+    i64 current_offset = 0;
+    bh__file_seek_wrapper(file->fd, 0, BH_FILE_WHENCE_CURRENT, &current_offset);
+
+#if defined(_BH_WINDOWS)
+    if (current_offset != offset) bh_file_seek_to(file, offset);
+    res = (isize) WriteFile(file->fd, buffer, buff_size, (i32 *) bytes_wrote, NULL);
+    return res;
+
+#elif defined(_BH_LINUX)
+    if (current_offset == offset || file->fd == 1 || file->fd == 2) {
+        // Standard in and out do like pwrite()
+        res = write(file->fd, buffer, buff_size);
+    } else {
+        res = pwrite(file->fd, buffer, buff_size, offset);
+    }
+    if (res < 0) return 0;
+    if (bytes_wrote) *bytes_wrote = res;
+
+    return 1;
+#endif
+}
+
+static b32 bh__file_seek_wrapper(bh_file_descriptor fd, i64 offset, bh_file_whence whence, i64* new_offset) {
+#if defined(_BH_WINDOWS)
+    LARGE_INTEGER new_file_pointer;
+    LARGE_INTEGER dest;
+    dest.QuadPart = offset;
+
+    BOOL res = SetFilePointerEx(fd, dest, &new_file_pointer, whence);
+    *new_offset = new_file_pointer.QuadPart;
+
+    return res;
+
+#elif defined(_BH_LINUX)
+    i64 res = lseek64(fd, offset, whence);
+    if (res < 0) return 0;
+    if (new_offset) *new_offset = res;
+    return 1;
+#endif
+}
+
+// Returns new offset
+i64 bh_file_seek(bh_file* file, i64 offset, bh_file_whence whence) {
+    i64 new_offset = -1;
+    bh__file_seek_wrapper(file->fd, offset, whence, &new_offset);
+    return new_offset;
+}
+
+i64 bh_file_seek_to(bh_file* file, i64 offset) {
+    i64 new_offset = -1;
+    bh__file_seek_wrapper(file->fd, offset, BH_FILE_WHENCE_BEGIN, &new_offset);
+    return new_offset;
+}
+
+i64 bh_file_seek_to_end(bh_file* file) {
+    i64 new_offset = -1;
+    bh__file_seek_wrapper(file->fd, 0, BH_FILE_WHENCE_END, &new_offset);
+    return new_offset;
+}
+
+i64 bh_file_skip(bh_file* file, i64 bytes) {
+    i64 new_offset = 0;
+    bh__file_seek_wrapper(file->fd, bytes, BH_FILE_WHENCE_CURRENT, &new_offset);
+    return new_offset;
+}
+
+i64 bh_file_tell(bh_file* file) {
+    i64 new_offset = 0;
+    bh__file_seek_wrapper(file->fd, 0, BH_FILE_WHENCE_CURRENT, &new_offset);
+    return new_offset;
+}
+
+bh_file_error bh_file_close(bh_file* file) {
+    bh_file_error err = BH_FILE_ERROR_NONE;
+
+#if defined(_BH_WINDOWS)
+    BOOL success = CloseHandle(file->fd);
+    if (!success) err = BH_FILE_ERROR_INVALID;
+
+    return err;
+
+#elif defined(_BH_LINUX)
+    i32 res = close(file->fd);
+    if (res < 0)
+        err = BH_FILE_ERROR_INVALID;
+
+    return err;
+#endif
+}
+
+b32 bh_file_read(bh_file* file, void* buffer, isize buff_size) {
+    return bh_file_read_at(file, bh_file_tell(file), buffer, buff_size, NULL);
+}
+
+b32 bh_file_write(bh_file* file, void* buffer, isize buff_size) {
+    return bh_file_write_at(file, bh_file_tell(file), buffer, buff_size, NULL);
+}
+
+void bh_file_flush(bh_file* file) {
+    #ifdef _BH_LINUX
+    fdatasync(file->fd);
+    #endif
+}
+
+i64 bh_file_size(bh_file* file) {
+    i64 size = 0;
+    i64 prev = bh_file_tell(file);
+    bh_file_seek_to_end(file);
+    size = bh_file_tell(file);
+    bh_file_seek_to(file, prev);
+    return size;
+}
+
+bh_file_contents bh_file_read_contents_bh_file(bh_allocator alloc, bh_file* file) {
+    bh_file_contents fc = {
+        .allocator = alloc,
+        .filename  = bh_strdup(alloc, (char *) file->filename),
+        .length = 0, .data = NULL, .line_count = 0,
+    };
+
+    isize size = bh_file_size(file);
+    if (size <= 0) return fc;
+
+    fc.data = bh_alloc(alloc, size + 1);
+    fc.length = size;
+    bh_file_read_at(file, 0, fc.data, fc.length, NULL);
+    ((u8*) fc.data)[fc.length] = '\0';
+
+    return fc;
+}
+
+bh_file_contents bh_file_read_contents_direct(bh_allocator alloc, const char* filename) {
+    bh_file file;
+    bh_file_open(&file, filename);
+    bh_file_contents fc = bh_file_read_contents(alloc, &file);
+    bh_file_close(&file);
+    return fc;
+}
+
+b32 bh_file_contents_free(bh_file_contents* contents) {
+    bh_free(contents->allocator, contents->data);
+    contents->length = 0;
+    return 1;
+}
+
+b32 bh_file_stat(char const* filename, bh_file_stats* out) {
+    struct stat s;
+    if (stat(filename, &s) == -1) {
+        return 0;
+    }
+
+    out->size = s.st_size;
+
+    if ((s.st_mode & S_IFMT) == S_IFDIR) out->file_type = BH_FILE_TYPE_DIRECTORY;
+    if ((s.st_mode & S_IFMT) == S_IFREG) out->file_type = BH_FILE_TYPE_FILE;
+
+#if defined(_BH_LINUX) 
+    if ((s.st_mode & S_IFMT) == S_IFLNK) out->file_type = BH_FILE_TYPE_LINK;
+#endif
+
+    return 1;
+}
+
+b32 bh_file_exists(char const* filename) {
+    struct stat s;
+    return stat(filename, &s) != -1;
+}
+
+b32 bh_file_remove(char const* filename) {
+#if defined(_BH_WINDOWS)
+    return DeleteFileA(filename);
+
+#elif defined(_BH_LINUX)
+    return unlink(filename) == 0;
+#endif
+}
+
+char* bh_path_get_full_name(char const* filename, bh_allocator a) {
+#if defined(_BH_WINDOWS)
+    char buffer[4096];
+    GetFullPathNameA(filename, 4096, buffer, NULL);
+
+    i32 len = strlen(buffer);
+    char* result = bh_alloc_array(a, char, len + 1);
+    memmove(result, buffer, len);
+    result[len] = 0;
+
+    return result;
+
+#elif defined(_BH_LINUX)
+    char* p = realpath(filename, NULL);    
+
+    // Check if the file did not exists.
+    // :Cleanup should this return NULL?
+    if (p == NULL) return (char *) filename;
+    
+    i32 len = strlen(p);
+    char* result = bh_alloc_array(a, char, len + 1);
+    memmove(result, p, len);
+    result[len] = 0;
+
+    free(p);
+
+    return result;
+#endif
+}
+
+// NOTE: This assumes the filename is the full path, not relative to anything else.
+#if defined(_BH_LINUX)
+    #define DIR_SEPARATOR '/'
+#elif defined(_BH_WINDOWS)
+    #define DIR_SEPARATOR '\\'
+#endif
+char* bh_path_get_parent(char const* filename, bh_allocator a) {
+
+    char* result = bh_strdup(a, (char *) filename);
+    char* end = result + strlen(result);
+    while (*end != DIR_SEPARATOR && end != result) *end-- = '\0';
+
+    return result;
+}
+
+// This function returns a volatile pointer. Do not store it without copying!
+char* bh_lookup_file(char* filename, char* relative_to, char *suffix, b32 add_suffix, bh_arr(const char *) included_folders, b32 search_included_folders) {
+    assert(relative_to != NULL);
+
+    static char path[512];
+    fori (i, 0, 512) path[i] = 0;
+
+    static char fn[256];
+    fori (i, 0, 256) fn[i] = 0;
+
+    if (!bh_str_ends_with(filename, suffix) && add_suffix) {
+        bh_snprintf(fn, 256, "%s%s", filename, suffix);
+    } else {
+        bh_snprintf(fn, 256, "%s", filename);
+    }
+
+    fori (i, 0, 256) if (fn[i] == '/') fn[i] = DIR_SEPARATOR;
+
+    if (bh_str_starts_with(filename, "./")) {
+        if (relative_to[strlen(relative_to) - 1] != DIR_SEPARATOR)
+            bh_snprintf(path, 512, "%s%c%s", relative_to, DIR_SEPARATOR, fn + 2);
+        else
+            bh_snprintf(path, 512, "%s%s", relative_to, fn + 2);
+
+        if (bh_file_exists(path)) return bh_path_get_full_name(path, bh_heap_allocator());
+
+        return fn;
+    }
+
+    if (search_included_folders) {
+        bh_arr_each(const char *, folder, included_folders) {
+            if ((*folder)[strlen(*folder) - 1] != DIR_SEPARATOR)
+                bh_snprintf(path, 512, "%s%c%s", *folder, DIR_SEPARATOR, fn);
+            else
+                bh_snprintf(path, 512, "%s%s", *folder, fn);
+
+            if (bh_file_exists(path)) return bh_path_get_full_name(path, bh_heap_allocator());
+        }
+    }
+
+    return fn;
+}
+
+//
+// Modifies the path in-place.
+char* bh_path_convert_separators(char* path) {
+#if defined(_BH_LINUX)
+    #define DIR_SEPARATOR '/'
+    #define OTHER_SEPARATOR '\\'
+#elif defined(_BH_WINDOWS)
+    #define DIR_SEPARATOR '\\'
+    #define OTHER_SEPARATOR '/'
+#endif
+
+    fori (i, 0, (i64) strlen(path)) {
+        if (path[i] == OTHER_SEPARATOR) {
+            path[i] = DIR_SEPARATOR;
+        }
+    }
+
+    return path;
+}
+
+
+bh_dir bh_dir_open(char* path) {
+#ifdef _BH_WINDOWS
+    char new_path[512] = { 0 };
+    strncpy(new_path, path, 511);
+    bh_path_convert_separators(new_path);
+    strncat(new_path, "\\*.*", 511);
+
+    Windows_Directory_Opened* dir = malloc(sizeof(Windows_Directory_Opened));
+    dir->hndl = FindFirstFileA(new_path, &dir->found_file);
+    if (dir->hndl == INVALID_HANDLE_VALUE) {
+        return NULL;
+    }
+
+    return dir;
+#endif
+
+#ifdef _BH_LINUX
+    DIR* dir = opendir(path);
+    return dir;
+#endif
+}
+
+b32 bh_dir_read(bh_dir dir, bh_dirent* out) {
+
+#ifdef _BH_WINDOWS
+    do {
+        BOOL success = FindNextFileA(dir->hndl, &dir->found_file);
+        if (!success) return 0;
+    } while (!strcmp(dir->found_file.cFileName, ".") || !strcmp(dir->found_file.cFileName, ".."));
+
+    if (out == NULL) return 1;
+
+    out->type = (dir->found_file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+        ? BH_DIRENT_DIRECTORY : BH_DIRENT_FILE;
+    out->id = 0;
+    out->name_length = strlen(dir->found_file.cFileName);
+    strncpy(out->name, dir->found_file.cFileName, 256);
+
+    return 1;
+#endif
+
+#ifdef _BH_LINUX
+    struct dirent *ent;
+    while (1) {
+        ent = readdir(dir);
+        if (ent == NULL) return 0;
+
+        // Skip the current directory and parent directory
+        if (strcmp(ent->d_name, ".") && strcmp(ent->d_name, "..")) break;
+    }
+
+    bh_dirent_type type = 0;
+    switch (ent->d_type) {
+        case DT_UNKNOWN: break;
+        case DT_BLK: type = BH_DIRENT_BLOCK; break;
+        case DT_CHR: type = BH_DIRENT_CHAR; break;
+        case DT_DIR: type = BH_DIRENT_DIRECTORY; break;
+        case DT_LNK: type = BH_DIRENT_SYMLINK; break;
+        case DT_REG: type = BH_DIRENT_FILE; break;
+        default: type = BH_DIRENT_OTHER; break;
+    }
+
+    if (out == NULL) return 1;
+
+    out->type = type;
+    out->id = (u32) ent->d_ino;
+    out->name_length = strlen(ent->d_name);
+    strncpy(out->name, ent->d_name, 256);
+
+    return 1;
+#endif
+}
+
+void bh_dir_close(bh_dir dir) {
+#ifdef _BH_WINDOWS
+    if (dir == NULL) return;
+
+    FindClose(dir->hndl);
+    free(dir);
+#endif
+
+#ifdef _BH_LINUX
+    if (dir == NULL) return;
+    closedir(dir);
+#endif
+}
+
+#undef DIR_SEPARATOR
+
+#endif // ifndef BH_NO_FILE
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// ALTERNATE PRINTF IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+isize bh_printf(char const *fmt, ...) {
+    isize res;
+    va_list va;
+    va_start(va, fmt);
+    res = bh_printf_va(fmt, va);
+    va_end(va);
+    return res;
+}
+
+isize bh_printf_va(char const *fmt, va_list va) {
+    bh_file file;
+    bh_file_get_standard(&file, BH_FILE_STANDARD_OUTPUT);
+    return bh_fprintf_va(&file, fmt, va);
+}
+
+isize bh_printf_err(char const *fmt, ...) {
+    isize res;
+    va_list va;
+    va_start(va, fmt);
+    res = bh_printf_err_va(fmt, va);
+    va_end(va);
+    return res;
+}
+
+isize bh_printf_err_va(char const *fmt, va_list va) {
+    bh_file file;
+    bh_file_get_standard(&file, BH_FILE_STANDARD_ERROR);
+    return bh_fprintf_va(&file, fmt, va);
+}
+
+isize bh_fprintf(bh_file* f, char const *fmt, ...) {
+    isize res;
+    va_list va;
+    va_start(va, fmt);
+    res = bh_fprintf_va(f, fmt, va);
+    va_end(va);
+    return res;
+}
+
+isize bh_fprintf_va(bh_file* f, char const *fmt, va_list va) {
+    static char buffer[4096];
+    isize len = bh_snprintf_va(buffer, sizeof(buffer), fmt, va);
+    bh_file_write(f, buffer, len - 1);
+    return len;
+}
+
+char* bh_bprintf(char const *fmt, ...) {
+    char* res;
+    va_list va;
+    va_start(va, fmt);
+    res = bh_bprintf_va(fmt, va);
+    va_end(va);
+    return res;
+}
+
+char* bh_bprintf_va(char const *fmt, va_list va) {
+    static char buffer[4096];
+    isize len = bh_snprintf_va(buffer, sizeof(buffer), fmt, va);
+    buffer[len - 1] = 0;
+    return buffer;
+}
+
+char* bh_aprintf(bh_allocator alloc, const char* fmt, ...) {
+    char* res;
+    va_list va;
+    va_start(va, fmt);
+    res = bh_aprintf_va(alloc, fmt, va);
+    va_end(va);
+    return res;
+}
+
+char* bh_aprintf_va(bh_allocator alloc, const char* fmt, va_list va) {
+    static char buffer[4096];
+    isize len = bh_snprintf_va(buffer, sizeof(buffer), fmt, va);
+    char* res = bh_alloc(alloc, len);
+    memcpy(res, buffer, len);
+    res[len - 1] = 0;
+    return res;
+}
+
+isize bh_snprintf(char *str, isize n, char const *fmt, ...) {
+    isize res;
+    va_list va;
+    va_start(va, fmt);
+    res = bh_snprintf_va(str, n, fmt, va);
+    va_end(va);
+    return res;
+}
+
+isize bh__print_string(char* dest, isize n, char* src) {
+    isize len = 0;
+    while (n-- && (*dest++ = *src++)) len++;
+    return len;
+}
+
+isize bh__printu64(char* str, isize n, bh__print_format format, u64 value) {
+    char buf[128];
+    buf[127] = 0;
+    char* walker = buf + 127;
+    u32 base = format.base ? format.base : 10, tmp;
+
+    while (value > 0) {
+        tmp = value % base;
+        if (tmp > 9) {
+            switch (tmp) {
+            case 10: tmp = 'a'; break;
+            case 11: tmp = 'b'; break;
+            case 12: tmp = 'c'; break;
+            case 13: tmp = 'd'; break;
+            case 14: tmp = 'e'; break;
+            case 15: tmp = 'f'; break;
+            }
+        } else {
+            tmp += '0';
+        }
+
+        *--walker = tmp;
+        value /= base;
+    }
+
+    if (format.base == 16) {
+        *--walker = 'x';
+        *--walker = '0';
+    }
+
+    return bh__print_string(str, n, walker);
+}
+
+isize bh__printi64(char* str, isize n, bh__print_format format, i64 value) {
+    char buf[128];
+    buf[127] = 0;
+    char* walker = buf + 127;
+    u32 base = format.base ? format.base : 10, tmp;
+
+    b32 negative = value < 0 ? 1 : 0;
+    if (negative) value = -value;
+
+    if (value == 0) {
+        *--walker = '0';
+    } else {
+        while (value > 0) {
+            tmp = value % base;
+            if (tmp > 9) {
+                switch (tmp) {
+                case 10: tmp = 'a'; break;
+                case 11: tmp = 'b'; break;
+                case 12: tmp = 'c'; break;
+                case 13: tmp = 'd'; break;
+                case 14: tmp = 'e'; break;
+                case 15: tmp = 'f'; break;
+                }
+            } else {
+                tmp += '0';
+            }
+
+            *--walker = tmp;
+            value /= base;
+        }
+    }
+
+    if (negative) {
+        *--walker = '-';
+    }
+
+    if (format.base == 16) {
+        *--walker = 'x';
+        *--walker = '0';
+    }
+
+    return bh__print_string(str, n, walker);
+}
+
+// TODO: This implementation is VERY VERY BAD AND WRONG. Fix it.
+isize bh__printf64(char* str, isize n, f64 value) {
+    fori (i, 0, 6) value *= 10.0;
+    i64 v = (i64) value;
+
+    isize l1 = bh__printi64(str, n, ((bh__print_format) { .base = 10 }), v / 1000000);
+    str += l1;
+    n -= l1;
+
+    *str = '.';
+    str += 1;
+    n -= 1;
+
+    isize l2 = bh__printi64(str, n, ((bh__print_format) { .base = 10 }), bh_abs(v) % 1000000);
+
+    return l1 + l2 + 1;
+}
+
+// TODO: This is very hacked together but for now it will work.
+isize bh_snprintf_va(char *str, isize n, char const *fmt, va_list va) {
+    char const *text_start = str;
+    isize res;
+
+    while (*fmt) {
+        bh__print_format format = { 0 };
+        isize len = 0;
+
+        while (*fmt && *fmt != '%') {
+            *(str++) = *(fmt++);
+        }
+
+        if (!*fmt) goto end_of_format;
+
+        fmt++;
+
+        switch (*fmt++) {
+        case 'o': format.base = 8; break;
+        case 'x': format.base = 16; break;
+        default: fmt--;
+        }
+
+        switch (*fmt) {
+        case 'c': {
+            char c = (char) va_arg(va, int);
+            *(str++) = c;
+        } break;
+
+        case 'd': {
+            i64 value = (i64) va_arg(va, int);
+            len = bh__printi64(str, n, format, value);
+        } break;
+
+        case 'l': {
+            i64 value = (i64) va_arg(va, long);
+            len = bh__printi64(str, n, format, value);
+        } break;
+
+        case 'p': {
+            u64 value = (u64) va_arg(va, ptr);
+            format.base = 16;
+            len = bh__printu64(str, n, format, value);
+        } break;
+
+        case 's': {
+            char* s = va_arg(va, char *);
+            len = bh__print_string(str, n, s);
+        } break;
+
+        case 'b': { // String with a length (not null terminated)
+            char* s = va_arg(va, char *);
+            i32 l = va_arg(va, int);
+            len = bh__print_string(str, bh_min(l, n), s);
+        } break;
+
+        case 'f': {
+            f64 f = va_arg(va, f64);
+            len = bh__printf64(str, n, f);
+        } break;
+
+        default: fmt--;
+        }
+
+        fmt++;
+
+end_of_format:
+        str += len;
+        n -= len;
+    }
+
+    return str - text_start + 1;
+}
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// FLEXIBLE BUFFER IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_BUFFER
+
+void bh_buffer_init(bh_buffer* buffer, bh_allocator alloc, i32 init_size) {
+    buffer->allocator = alloc;
+    buffer->length = 0;
+    buffer->capacity = init_size;
+    buffer->data = bh_alloc(alloc, init_size);
+}
+
+void bh_buffer_free(bh_buffer* buffer) {
+    bh_free(buffer->allocator, buffer->data);
+    buffer->length = 0;
+    buffer->capacity = 0;
+}
+
+void bh_buffer_clear(bh_buffer* buffer) {
+    buffer->length = 0;
+}
+
+void bh_buffer_grow(bh_buffer* buffer, i32 length) {
+    if (buffer == NULL) return;
+
+    if (buffer->capacity >= length) {
+        // NOTE: Already have enough room
+        return;
+    }
+
+    i32 newcap = buffer->capacity;
+    while (newcap < length) newcap = BH_BUFFER_GROW_FORMULA(newcap);
+
+    ptr new_data = bh_resize(buffer->allocator, buffer->data, newcap);
+    if (new_data == NULL) return;
+
+    buffer->capacity = newcap;
+    buffer->data = new_data;
+}
+
+void bh_buffer_append(bh_buffer* buffer, const void * data, i32 length) {
+    if (buffer == NULL) return;
+
+    if (buffer->length + length > buffer->capacity) {
+        bh_buffer_grow(buffer, buffer->length + length);
+    }
+
+    memcpy(buffer->data + buffer->length, data, length);
+    buffer->length += length;
+}
+
+void bh_buffer_concat(bh_buffer* buffer, bh_buffer other) {
+    bh_buffer_append(buffer, other.data, other.length);
+}
+
+void bh_buffer_write_byte(bh_buffer* buffer, u8 byte) {
+    bh_buffer_grow(buffer, buffer->length + 1);
+    buffer->data[buffer->length++] = byte;
+}
+
+void bh_buffer_write_u32(bh_buffer* buffer, u32 i) {
+    bh_buffer_grow(buffer, buffer->length + 4);
+    *((u32 *) bh_pointer_add(buffer->data, buffer->length)) = i;
+    buffer->length += 4;
+}
+
+void bh_buffer_write_u64(bh_buffer* buffer, u64 i) {
+    bh_buffer_grow(buffer, buffer->length + 8);
+    *((u64 *) bh_pointer_add(buffer->data, buffer->length)) = i;
+    buffer->length += 8;
+}
+
+void bh_buffer_align(bh_buffer* buffer, u32 alignment) {
+    if (buffer->length % alignment != 0) {
+        u32 difference = alignment - (buffer->length % alignment);
+        buffer->length += difference;
+
+        bh_buffer_grow(buffer, buffer->length);
+    }
+}
+
+
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// ARRAY IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_ARRAY
+
+b32 bh__arr_grow(bh_allocator alloc, void** arr, i32 elemsize, i32 cap) {
+    bh__arr* arrptr;
+
+    if (*arr == NULL) {
+        if (cap == 0 && elemsize == 0) return 1;
+
+        arrptr = (bh__arr *) bh_alloc(alloc, sizeof(*arrptr) + elemsize * cap);
+        if (arrptr == NULL) return 0;
+
+        arrptr->allocator = alloc;
+        arrptr->capacity = cap;
+        arrptr->length = 0;
+
+    } else {
+        arrptr = bh__arrhead(*arr);
+
+        if (arrptr->capacity < cap) {
+            void* p;
+            i32 newcap = arrptr->capacity;
+            while (newcap < cap) newcap = BH_ARR_GROW_FORMULA(newcap);
+
+            p = bh_resize(arrptr->allocator, arrptr, sizeof(*arrptr) + elemsize * newcap);
+
+            if (p) {
+                arrptr = (bh__arr *) p;
+                arrptr->capacity = newcap;
+            } else {
+                return 0;
+            }
+        }
+    }
+
+    *arr = arrptr + 1;
+    return 1;
+}
+
+b32 bh__arr_shrink(void** arr, i32 elemsize, i32 cap) {
+    if (*arr == NULL) return 0;
+
+    bh__arr* arrptr = bh__arrhead(*arr);
+    cap = bh_max(cap, arrptr->length);
+
+    if (arrptr->capacity > cap) {
+        void* p = bh_resize(arrptr->allocator, arrptr, sizeof(*arrptr) + elemsize * cap);
+
+        if (p) {
+            arrptr = (bh__arr *) p;
+            arrptr->capacity = cap;
+        } else {
+            return 0;
+        }
+    }
+
+    *arr = arrptr + 1;
+    return 1;
+}
+
+b32 bh__arr_free(void **arr) {
+    if (*arr == NULL) return 0;
+
+    bh__arr* arrptr = bh__arrhead(*arr);
+    bh_free(arrptr->allocator, arrptr);
+    *arr = NULL;
+    return 1;
+}
+
+void* bh__arr_copy(bh_allocator alloc, void *arr, i32 elemsize) {
+    bh__arr* arrptr = bh__arrhead(arr);
+
+    const i32 cap = arrptr->length;
+
+    void* newarr = NULL;
+    bh__arr_grow(alloc, &newarr, elemsize, cap);
+    bh__arrhead(newarr)->length = cap;
+    bh__arrhead(newarr)->capacity = cap;
+    memcpy(newarr, arr, elemsize * arrptr->length);
+
+    return newarr;
+}
+
+void bh__arr_deleten(void **arr, i32 elemsize, i32 index, i32 numelems) {
+    bh__arr* arrptr = bh__arrhead(*arr);
+
+    if (index >= arrptr->length) return; // Can't delete past the end of the array
+    if (numelems <= 0) return; // Can't delete nothing
+
+    memmove(
+        (char *)(*arr) + elemsize * index,                    // Target
+        (char *)(*arr) + elemsize * (index + numelems),        // Source
+        elemsize * (arrptr->length - (index + numelems)));    // Length
+    arrptr->length -= numelems;
+}
+
+void bh__arr_insertn(void **arr, i32 elemsize, i32 index, i32 numelems) {
+    if (numelems) {
+        if (*arr == NULL) {
+            bh__arr_grow(bh_arr_allocator(arr), arr, elemsize, numelems); // Making a new array
+            return;
+        }
+
+        bh__arr* arrptr = bh__arrhead(*arr);
+        if (!bh__arr_grow(bh_arr_allocator(arr), arr, elemsize, arrptr->length + numelems)) return; // Fail case
+        arrptr = bh__arrhead(*arr);
+        memmove(
+            (char *)(*arr) + elemsize * (index + numelems),
+            (char *)(*arr) + elemsize * index,
+            elemsize * (arrptr->length - index));
+        arrptr->length += numelems;
+    }
+}
+
+#endif // ifndef BH_NO_ARRAY
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// TABLE IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_TABLE
+
+b32 bh__table_init(bh_allocator allocator, bh__table **table, i32 table_size) {
+    *table = bh_alloc(allocator, sizeof(bh__table) + sizeof(ptr) * table_size);
+    if (*table == NULL) return 0;
+
+    (*table)->allocator = allocator;
+    (*table)->table_size = table_size;
+
+    for (i32 i = 0; i < table_size; i++) {
+        (*table)->arrs[i] = NULL;
+    }
+
+    return 1;
+}
+
+b32 bh__table_free(bh__table **table) {
+    if (*table == NULL) return 0;
+
+    for (u64 i = 0; i < (*table)->table_size; i++) {
+        if ((*table)->arrs[i] != NULL) {
+            bh_arr_free((*table)->arrs[i]);
+        }
+    }
+
+    bh_free((*table)->allocator, *table);
+    *table = NULL;
+    return 1;
+}
+
+// Assumes NULL terminated string for key
+ptr bh__table_put(bh__table *table, i32 elemsize, char *key) {
+    elemsize += (elemsize & 1);
+
+    u64 index = bh__table_hash_function(key, 0, table->table_size);
+    u8 arr_was_new = 0;
+
+    ptr arrptr = table->arrs[index];
+    if (arrptr == NULL) {
+        arr_was_new = 1;
+        goto add_new_element;
+    }
+    u64 len = *(u64 *) arrptr;
+    arrptr = bh_pointer_add(arrptr, sizeof(u64));
+
+    u16 key_length = 0;
+    while (len--) {
+        arrptr = bh_pointer_add(arrptr, elemsize);
+        key_length = *(u16 *) arrptr;
+        arrptr = bh_pointer_add(arrptr, sizeof(u16));
+        if (strncmp(key, (char *) arrptr, key_length) == 0) goto found_matching;
+        arrptr = bh_pointer_add(arrptr, key_length);
+    }
+
+add_new_element:
+    arrptr = table->arrs[index];
+    i32 byte_len = bh_arr_length(arrptr);
+    if (byte_len == 0) byte_len = sizeof(u64);
+    key_length = strlen(key) + 1;
+
+    // NOTE: Align to 16 bytes
+    if ((key_length + 2) % 16 != 0) {
+        key_length = ((((key_length + 2) >> 4) + 1) << 4) - 2;
+    }
+
+    bh__arr_grow(table->allocator, &arrptr, 1, byte_len + elemsize + sizeof(u16) + key_length);
+    bh__arrhead(arrptr)->length = byte_len + elemsize + sizeof(u16) + key_length;
+    table->arrs[index] = arrptr;
+
+    if (arr_was_new) {
+        *(u64 *) arrptr = 1;
+    } else {
+        (*(u64 *) arrptr)++;
+    }
+
+    arrptr = bh_pointer_add(arrptr, byte_len + elemsize);
+    *(u16 *) arrptr = key_length;
+    arrptr = bh_pointer_add(arrptr, sizeof(u16));
+    strncpy(arrptr, key, key_length);
+
+found_matching:
+    return bh_pointer_add(arrptr, -(sizeof(u16) + elemsize));
+}
+
+b32 bh__table_has(bh__table *table, i32 elemsize, char *key) {
+    elemsize += (elemsize & 1);
+
+    u64 index = bh__table_hash_function(key, 0, table->table_size);
+
+    ptr arrptr = table->arrs[index];
+    if (arrptr == NULL) return 0;
+
+    u64 len = *(u64 *) arrptr;
+    arrptr = bh_pointer_add(arrptr, sizeof(u64));
+
+    u16 key_length = 0;
+    while (len--) {
+        arrptr = bh_pointer_add(arrptr, elemsize);
+        key_length = *(u16 *) arrptr;
+        arrptr = bh_pointer_add(arrptr, sizeof(u16));
+        if (strncmp(key, (char *) arrptr, key_length) == 0) return 1;
+        arrptr = bh_pointer_add(arrptr, key_length);
+    }
+
+    return 0;
+}
+
+ptr bh__table_get(bh__table *table, i32 elemsize, char *key) {
+    elemsize += (elemsize & 1);
+
+    u64 index = bh__table_hash_function(key, 0, table->table_size);
+
+    ptr arrptr = table->arrs[index];
+    if (arrptr == NULL) return 0;
+
+    u64 len = *(u64 *) arrptr;
+    arrptr = bh_pointer_add(arrptr, sizeof(u64));
+
+    u16 key_length = 0;
+    while (len--) {
+        arrptr = bh_pointer_add(arrptr, elemsize);
+        key_length = *(u16 *) arrptr;
+        arrptr = bh_pointer_add(arrptr, sizeof(u16));
+        if (strncmp(key, (char *) arrptr, key_length) == 0) {
+            return bh_pointer_add(arrptr, -(sizeof(u16) + elemsize));
+        }
+        arrptr = bh_pointer_add(arrptr, key_length);
+    }
+
+    return NULL;
+}
+
+void bh__table_delete(bh__table *table, i32 elemsize, char *key) {
+    elemsize += (elemsize & 1);
+
+    u64 index = bh__table_hash_function(key, 0, table->table_size);
+
+    ptr arrptr = table->arrs[index], walker;
+    if (arrptr == NULL) return; // Didn't exist
+    walker = arrptr;
+
+    i32 byte_offset = 8;
+    i32 delete_len = 0;
+
+    u64 len = *(u64 *) walker;
+    walker = bh_pointer_add(walker, sizeof(u64));
+
+    u16 key_length = 0;
+    while (len--) {
+        walker = bh_pointer_add(walker, elemsize);
+        key_length = *(u16 *) walker;
+        walker = bh_pointer_add(walker, sizeof(u16));
+        if (strncmp(key, (char *) walker, key_length) == 0) {
+            delete_len = elemsize + sizeof(u16) + key_length;
+            goto found_matching;
+        }
+        walker = bh_pointer_add(walker, key_length);
+        byte_offset += elemsize + sizeof(u16) + key_length;
+    }
+
+    // NOTE: Already didn't exist
+    return;
+
+found_matching:
+    bh__arr_deleten((void **) &arrptr, 1, byte_offset, delete_len);
+    table->arrs[index] = arrptr;
+    (*(u64 *) arrptr)--;
+}
+
+void bh__table_clear(bh__table *table) {
+    for (u64 i = 0; i < table->table_size; i++) {
+        if (table->arrs[i] != NULL) {
+            // NOTE: Set length property to 0
+            *((u64 *) table->arrs[i]) = 0;
+            bh_arr_set_length(table->arrs[i], 0);
+        }
+    }
+}
+
+bh_table_iterator bh__table_iter_setup(bh__table *table, i32 elemsize) {
+    elemsize += (elemsize & 1);
+
+    bh_table_iterator it = {
+        .tab = table->arrs,
+        .endtab = table->arrs + table->table_size,
+        .elemsize = elemsize,
+        .entry = NULL
+    };
+    return it;
+}
+
+b32 bh_table_iter_next(bh_table_iterator* it) {
+    if (it->tab == NULL) return 0;
+
+    if (it->entry != NULL) {
+        it->arrlen--;
+        if (it->arrlen <= 0) {
+            it->tab++;
+            goto step_to_next;
+        }
+
+        it->entry = bh_pointer_add(it->entry, it->elemsize);
+        it->entry = bh_pointer_add(it->entry, sizeof(u16) + (*(u16 *) it->entry));
+        return 1;
+    }
+
+step_to_next:
+    // Step forward to find next valid
+    while (it->tab != it->endtab && *it->tab == NULL) {
+        it->tab++;
+    }
+
+    if (it->tab == it->endtab) return 0;
+
+    it->entry = *it->tab;
+    it->arrlen = *(u64 *) it->entry;
+    it->entry = bh_pointer_add(it->entry, sizeof(u64));
+    if (it->arrlen <= 0) {
+        it->tab++;
+        goto step_to_next;
+    }
+    return 1;
+}
+
+#endif // ifndef BH_NO_HASHTABLE
+
+
+
+//-------------------------------------------------------------------------------------
+// IMAP IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_IMAP
+void bh_imap_init(bh_imap* imap, bh_allocator alloc, i32 hash_count) {
+    imap->allocator = alloc;
+
+    imap->hashes  = NULL;
+    imap->entries = NULL;
+
+    bh_arr_new(alloc, imap->hashes, hash_count);
+    bh_arr_new(alloc, imap->entries, 4);
+
+    fori(count, 0, hash_count) bh_arr_push(imap->hashes, -1);
+}
+
+void bh_imap_free(bh_imap* imap) {
+    bh_arr_free(imap->hashes);
+    bh_arr_free(imap->entries);
+
+    imap->hashes = NULL;
+    imap->entries = NULL;
+}
+
+bh__imap_lookup_result bh__imap_lookup(bh_imap* imap, bh_imap_entry_t key) {
+    bh__imap_lookup_result lr = { -1, -1, -1 };
+
+    u64 hash = 0xcbf29ce484222325ull ^ key;
+    u64 n = bh_arr_capacity(imap->hashes);
+
+    lr.hash_index  = hash % n;
+    lr.entry_index = imap->hashes[lr.hash_index];
+    while (lr.entry_index >= 0) {
+        if (imap->entries[lr.entry_index].key == key) {
+            return lr;
+        }
+
+        lr.entry_prev  = lr.entry_index;
+        lr.entry_index = imap->entries[lr.entry_index].next;
+    }
+
+    return lr;
+}
+
+void bh_imap_put(bh_imap* imap, bh_imap_entry_t key, bh_imap_entry_t value) {
+    bh__imap_lookup_result lr = bh__imap_lookup(imap, key);
+
+    if (lr.entry_index >= 0) {
+        imap->entries[lr.entry_index].value = value;
+        return;
+    }
+
+    bh__imap_entry entry;
+    entry.key = key;
+    entry.value = value;
+    entry.next = imap->hashes[lr.hash_index];
+    bh_arr_push(imap->entries, entry);
+
+    imap->hashes[lr.hash_index] = bh_arr_length(imap->entries) - 1;
+}
+
+b32 bh_imap_has(bh_imap* imap, bh_imap_entry_t key) {
+    bh__imap_lookup_result lr = bh__imap_lookup(imap, key);
+    return lr.entry_index >= 0;
+}
+
+bh_imap_entry_t bh_imap_get(bh_imap* imap, bh_imap_entry_t key) {
+    bh__imap_lookup_result lr = bh__imap_lookup(imap, key);
+    if (lr.entry_index >= 0) {
+        return imap->entries[lr.entry_index].value;
+    } else {
+        return 0;
+    }
+}
+
+void bh_imap_delete(bh_imap* imap, bh_imap_entry_t key) {
+    bh__imap_lookup_result lr = bh__imap_lookup(imap, key);
+    if (lr.entry_index < 0) return;
+
+    if (lr.entry_prev < 0) {
+        imap->hashes[lr.hash_index] = imap->entries[lr.entry_index].next;
+    } else {
+        imap->entries[lr.entry_prev].next = imap->entries[lr.entry_index].next;
+    }
+
+    // If it's that last thing in the array, just pop off the end
+    if (lr.entry_index == bh_arr_length(imap->entries) - 1) {
+        bh_arr_pop(imap->entries);
+        return;
+    }
+
+    bh_arr_fastdelete(imap->entries, lr.entry_index);
+    bh__imap_lookup_result last = bh__imap_lookup(imap, imap->entries[lr.entry_index].key);
+    if (last.entry_prev >= 0) {
+        imap->entries[last.entry_prev].next = lr.entry_index;
+    } else {
+        imap->hashes[last.hash_index] = lr.entry_index;
+    }
+}
+
+void bh_imap_clear(bh_imap* imap) {
+    // NOTE: Does not clear out an of the data that was in the map
+    bh_arr_each(i64, hash, imap->hashes) *hash = -1;
+    bh_arr_set_length(imap->entries, 0);
+}
+
+#endif // ifndef BH_NO_IMAP
+
+
+
+
+
+u64 bh_time_curr() {
+#if defined(_BH_WINDOWS)
+    LARGE_INTEGER result;
+    QueryPerformanceCounter(&result);
+    return (u64) result.QuadPart;
+
+#elif defined(_BH_LINUX)
+    struct timespec spec;
+    clock_gettime(CLOCK_REALTIME, &spec);
+
+    time_t sec = spec.tv_sec;
+    u64 ms  = spec.tv_nsec / 1000000;
+    if (ms > 999) {
+        sec++;
+        ms = 0;
+    }
+
+    return sec * 1000 + ms;
+#endif
+}
+
+u64 bh_time_duration(u64 old) {
+#if defined(_BH_WINDOWS)
+    u64 curr = bh_time_curr();
+    u64 duration = curr - old;
+    
+    LARGE_INTEGER freq;
+    QueryPerformanceFrequency(&freq);
+    duration *= 1000;
+    duration /= freq.QuadPart;
+    return duration;
+
+#elif defined(_BH_LINUX)
+    u64 curr = bh_time_curr();
+    return curr - old;
+#endif
+}
+
+#endif // ifdef BH_DEFINE
+
+#endif // ifndef BH_H
diff --git a/shared/include/onyx_library.h b/shared/include/onyx_library.h
new file mode 100644 (file)
index 0000000..573de30
--- /dev/null
@@ -0,0 +1,102 @@
+
+#include "wasm.h"
+
+#if defined(_WIN32) || defined(_WIN64)
+    #define ONYX_EXPORT extern __declspec(dllexport)
+    #define ONYX_IMPORT extern __declspec(dllimport)
+#endif
+
+#if defined(__unix__)
+    #define ONYX_EXPORT
+    #define ONYX_IMPORT
+#endif
+
+typedef struct OnyxRuntime {
+    wasm_instance_t* wasm_instance;
+    wasm_module_t* wasm_module;
+    wasm_memory_t* wasm_memory;
+    wasm_engine_t *wasm_engine;
+    wasm_extern_vec_t wasm_imports;
+
+    int argc;
+    char **argv;
+
+    // HACK HACK HACK
+    // There should need to be this much stuff in here, but because Wasmer doesn't ship a "wasmerdll.lib"
+    // file for windows, it is impossible for it to link successfully against the function provided in onyx.exe.
+    // Therefore, we must serve as the linker and do this manually. Hopefully that library file will be
+    // shipped soon so this can go away...
+    char* (*wasm_memory_data)(wasm_memory_t *wasm_memory);
+    wasm_extern_t* (*wasm_extern_lookup_by_name)(wasm_module_t* module, wasm_instance_t* instance, const char* name);
+    wasm_func_t* (*wasm_extern_as_func)(wasm_extern_t* ext);
+    wasm_trap_t* (*wasm_func_call)(const wasm_func_t* wasm_func, const wasm_val_vec_t* args, wasm_val_vec_t* results);
+    wasm_instance_t* (*wasm_instance_new)(wasm_store_t* store, const wasm_module_t* module, const wasm_extern_vec_t* imports, wasm_trap_t** traps);
+    void (*onyx_print_trap)(wasm_trap_t* trap);
+    wasm_store_t *(*wasm_store_new)(wasm_engine_t *engine);
+    void (*wasm_store_delete)(wasm_store_t *store);
+} OnyxRuntime;
+
+OnyxRuntime* runtime;
+
+typedef struct WasmValkindBuffer {
+    unsigned int count;
+    wasm_valkind_t types[20];
+} WasmValkindBuffer;
+
+typedef struct WasmFuncDefinition {
+    char* module_name;
+    char* import_name;
+    wasm_func_callback_t func;
+
+    WasmValkindBuffer *params;
+    WasmValkindBuffer *results;
+} WasmFuncDefinition;
+
+#define STRINGIFY1(a) #a
+#define STRINGIFY2(a) STRINGIFY1(a)
+#define CONCAT2(a, b) a ## _ ## b
+#define CONCAT3(a, b, c) a ## _ ## b ## _ ## c
+#define ONYX_MODULE_NAME_GEN(m) CONCAT2(__onyx_library, m)
+#define ONYX_LINK_NAME_GEN(m) CONCAT2(onyx_library, m)
+#define ONYX_FUNC_NAME(m, n) CONCAT3(__onyx_internal, m, n)
+#define ONYX_DEF_NAME(m, n) CONCAT3(__onyx_internal_def, m, n)
+#define ONYX_PARAM_NAME(m, n) CONCAT3(__onyx_internal_param_buffer, m, n)
+#define ONYX_RESULT_NAME(m, n) CONCAT3(__onyx_internal_result_buffer, m, n)
+#define ONYX_IMPORT_NAME(m, n) STRINGIFY1(m) "_" #n
+
+#define NUM_VALS(...) (sizeof((wasm_valkind_t []){ 0, __VA_ARGS__ }) / sizeof(wasm_valkind_t))
+#define _VALS(...) { NUM_VALS(__VA_ARGS__) - 1, __VA_ARGS__ }
+
+#define ONYX_DEF(name, params_types, result_types) \
+    static wasm_trap_t* ONYX_FUNC_NAME(ONYX_LIBRARY_NAME, name)(const wasm_val_vec_t* params, wasm_val_vec_t* results); \
+    static struct WasmValkindBuffer  ONYX_PARAM_NAME(ONYX_LIBRARY_NAME, name) = _VALS params_types; \
+    static struct WasmValkindBuffer  ONYX_RESULT_NAME(ONYX_LIBRARY_NAME, name) = _VALS result_types; \
+    static struct WasmFuncDefinition ONYX_DEF_NAME(ONYX_LIBRARY_NAME, name) = { STRINGIFY2(ONYX_LIBRARY_NAME), #name, ONYX_FUNC_NAME(ONYX_LIBRARY_NAME, name), & ONYX_PARAM_NAME(ONYX_LIBRARY_NAME, name), & ONYX_RESULT_NAME(ONYX_LIBRARY_NAME, name) }; \
+    \
+    static wasm_trap_t* ONYX_FUNC_NAME(ONYX_LIBRARY_NAME, name)(const wasm_val_vec_t* params, wasm_val_vec_t* results)
+
+#define ONYX_FUNC(name) & ONYX_DEF_NAME(ONYX_LIBRARY_NAME, name),
+#define ONYX_LIBRARY \
+    extern struct WasmFuncDefinition *ONYX_MODULE_NAME_GEN(ONYX_LIBRARY_NAME)[]; \
+    ONYX_EXPORT WasmFuncDefinition** ONYX_LINK_NAME_GEN(ONYX_LIBRARY_NAME)(OnyxRuntime* in_runtime) { \
+        runtime = in_runtime; \
+        return ONYX_MODULE_NAME_GEN(ONYX_LIBRARY_NAME); \
+    } \
+    struct WasmFuncDefinition *ONYX_MODULE_NAME_GEN(ONYX_LIBRARY_NAME)[] =
+
+// Shorter names
+#ifndef ONYX_NO_SHORT_NAMES
+#undef  BOOL
+#undef  INT
+#undef  LONG
+#undef  FLOAT
+#undef  DOUBLE
+#define BOOL WASM_I32
+#define INT WASM_I32
+#define LONG WASM_I64
+#define FLOAT WASM_F32
+#define DOUBLE WASM_F64
+#define PTR WASM_I32
+#endif
+
+#define ONYX_PTR(p) ((void*) (p != 0 ? (runtime->wasm_memory_data(runtime->wasm_memory) + p) : NULL))
diff --git a/shared/include/small_windows.h b/shared/include/small_windows.h
new file mode 100644 (file)
index 0000000..28cea0b
--- /dev/null
@@ -0,0 +1,444 @@
+// Bill's Mini Windows.h from https://github.com/odin-lang/Odin/blob/master/src/gb/gb.h
+
+////////////////////////////////////////////////////////////////
+//
+// Bill's Mini Windows.h
+//
+//
+
+#define GB_EXTERN extern
+#define GB_DLL_EXPORT GB_EXTERN __declspec(dllexport)
+#define GB_DLL_IMPORT GB_EXTERN __declspec(dllimport)
+
+#define WINAPI   __stdcall
+#define WINAPIV  __cdecl
+#define CALLBACK __stdcall
+#define MAX_PATH 260
+#define CCHDEVICENAME 32
+#define CCHFORMNAME   32
+
+typedef unsigned long DWORD;
+typedef int WINBOOL;
+#ifndef XFree86Server
+    #ifndef __OBJC__
+    typedef WINBOOL BOOL;
+    #else
+    #define BOOL WINBOOL
+    #endif
+typedef unsigned char BYTE;
+#endif
+typedef unsigned short WORD;
+typedef float FLOAT;
+typedef int INT;
+typedef unsigned int UINT;
+typedef short SHORT;
+typedef long LONG;
+typedef long long LONGLONG;
+typedef unsigned short USHORT;
+typedef unsigned long ULONG;
+typedef unsigned long long ULONGLONG;
+
+typedef UINT WPARAM;
+typedef LONG LPARAM;
+typedef LONG LRESULT;
+#ifndef _HRESULT_DEFINED
+typedef LONG HRESULT;
+#define _HRESULT_DEFINED
+#endif
+#ifndef XFree86Server
+typedef WORD ATOM;
+#endif /* XFree86Server */
+typedef void *HANDLE;
+typedef HANDLE HGLOBAL;
+typedef HANDLE HLOCAL;
+typedef HANDLE GLOBALHANDLE;
+typedef HANDLE LOCALHANDLE;
+typedef void *HGDIOBJ;
+
+#define DECLARE_HANDLE(name) typedef HANDLE name
+DECLARE_HANDLE(HACCEL);
+DECLARE_HANDLE(HBITMAP);
+DECLARE_HANDLE(HBRUSH);
+DECLARE_HANDLE(HCOLORSPACE);
+DECLARE_HANDLE(HDC);
+DECLARE_HANDLE(HGLRC);
+DECLARE_HANDLE(HDESK);
+DECLARE_HANDLE(HENHMETAFILE);
+DECLARE_HANDLE(HFONT);
+DECLARE_HANDLE(HICON);
+DECLARE_HANDLE(HKEY);
+typedef HKEY *PHKEY;
+DECLARE_HANDLE(HMENU);
+DECLARE_HANDLE(HMETAFILE);
+DECLARE_HANDLE(HINSTANCE);
+typedef HINSTANCE HMODULE;
+DECLARE_HANDLE(HPALETTE);
+DECLARE_HANDLE(HPEN);
+DECLARE_HANDLE(HRGN);
+DECLARE_HANDLE(HRSRC);
+DECLARE_HANDLE(HSTR);
+DECLARE_HANDLE(HTASK);
+DECLARE_HANDLE(HWND);
+DECLARE_HANDLE(HWINSTA);
+DECLARE_HANDLE(HKL);
+DECLARE_HANDLE(HRAWINPUT);
+DECLARE_HANDLE(HMONITOR);
+#undef DECLARE_HANDLE
+
+typedef int HFILE;
+typedef HICON HCURSOR;
+typedef DWORD COLORREF;
+typedef int (WINAPI *FARPROC)();
+typedef int (WINAPI *NEARPROC)();
+typedef int (WINAPI *PROC)();
+typedef LRESULT (CALLBACK *WNDPROC)(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+#if defined(_WIN64)
+typedef unsigned __int64 ULONG_PTR;
+typedef signed __int64 LONG_PTR;
+#else
+typedef unsigned long ULONG_PTR;
+typedef signed long LONG_PTR;
+#endif
+typedef ULONG_PTR DWORD_PTR;
+
+typedef struct tagRECT {
+    LONG left;
+    LONG top;
+    LONG right;
+    LONG bottom;
+} RECT;
+typedef struct tagRECTL {
+    LONG left;
+    LONG top;
+    LONG right;
+    LONG bottom;
+} RECTL;
+typedef struct tagPOINT {
+    LONG x;
+    LONG y;
+} POINT;
+typedef struct tagSIZE {
+    LONG cx;
+    LONG cy;
+} SIZE;
+typedef struct tagPOINTS {
+    SHORT x;
+    SHORT y;
+} POINTS;
+typedef struct _SECURITY_ATTRIBUTES {
+    DWORD  nLength;
+    HANDLE lpSecurityDescriptor;
+    BOOL   bInheritHandle;
+} SECURITY_ATTRIBUTES;
+typedef enum _LOGICAL_PROCESSOR_RELATIONSHIP {
+    RelationProcessorCore,
+    RelationNumaNode,
+    RelationCache,
+    RelationProcessorPackage,
+    RelationGroup,
+    RelationAll               = 0xffff
+} LOGICAL_PROCESSOR_RELATIONSHIP;
+typedef enum _PROCESSOR_CACHE_TYPE {
+    CacheUnified,
+    CacheInstruction,
+    CacheData,
+    CacheTrace
+} PROCESSOR_CACHE_TYPE;
+typedef struct _CACHE_DESCRIPTOR {
+    BYTE                 Level;
+    BYTE                 Associativity;
+    WORD                 LineSize;
+    DWORD                Size;
+    PROCESSOR_CACHE_TYPE Type;
+} CACHE_DESCRIPTOR;
+typedef struct _SYSTEM_LOGICAL_PROCESSOR_INFORMATION {
+    ULONG_PTR                       ProcessorMask;
+    LOGICAL_PROCESSOR_RELATIONSHIP Relationship;
+    union {
+        struct {
+            BYTE Flags;
+        } ProcessorCore;
+        struct {
+            DWORD NodeNumber;
+        } NumaNode;
+        CACHE_DESCRIPTOR Cache;
+        ULONGLONG        Reserved[2];
+    };
+} SYSTEM_LOGICAL_PROCESSOR_INFORMATION;
+typedef struct _MEMORY_BASIC_INFORMATION {
+    void *BaseAddress;
+    void *AllocationBase;
+    DWORD AllocationProtect;
+    size_t RegionSize;
+    DWORD State;
+    DWORD Protect;
+    DWORD Type;
+} MEMORY_BASIC_INFORMATION;
+typedef struct _SYSTEM_INFO {
+    union {
+        DWORD   dwOemId;
+        struct {
+            WORD wProcessorArchitecture;
+            WORD wReserved;
+        };
+    };
+    DWORD     dwPageSize;
+    void *    lpMinimumApplicationAddress;
+    void *    lpMaximumApplicationAddress;
+    DWORD_PTR dwActiveProcessorMask;
+    DWORD     dwNumberOfProcessors;
+    DWORD     dwProcessorType;
+    DWORD     dwAllocationGranularity;
+    WORD      wProcessorLevel;
+    WORD      wProcessorRevision;
+} SYSTEM_INFO;
+typedef union _LARGE_INTEGER {
+    struct {
+        DWORD LowPart;
+        LONG  HighPart;
+    };
+    struct {
+        DWORD LowPart;
+        LONG  HighPart;
+    } u;
+    LONGLONG QuadPart;
+} LARGE_INTEGER;
+typedef union _ULARGE_INTEGER {
+    struct {
+        DWORD LowPart;
+        DWORD HighPart;
+    };
+    struct {
+        DWORD LowPart;
+        DWORD HighPart;
+    } u;
+    ULONGLONG QuadPart;
+} ULARGE_INTEGER;
+
+typedef struct _OVERLAPPED {
+    ULONG_PTR Internal;
+    ULONG_PTR InternalHigh;
+    union {
+        struct {
+            DWORD Offset;
+            DWORD OffsetHigh;
+        };
+        void *Pointer;
+    };
+    HANDLE hEvent;
+} OVERLAPPED;
+typedef struct _FILETIME {
+    DWORD dwLowDateTime;
+    DWORD dwHighDateTime;
+} FILETIME;
+typedef struct _WIN32_FIND_DATAW {
+    DWORD    dwFileAttributes;
+    FILETIME ftCreationTime;
+    FILETIME ftLastAccessTime;
+    FILETIME ftLastWriteTime;
+    DWORD    nFileSizeHigh;
+    DWORD    nFileSizeLow;
+    DWORD    dwReserved0;
+    DWORD    dwReserved1;
+    wchar_t  cFileName[MAX_PATH];
+    wchar_t  cAlternateFileName[14];
+} WIN32_FIND_DATAW;
+typedef struct _WIN32_FIND_DATAA {
+    DWORD    dwFileAttributes;
+    FILETIME ftCreationTime;
+    FILETIME ftLastAccessTime;
+    FILETIME ftLastWriteTime;
+    DWORD    nFileSizeHigh;
+    DWORD    nFileSizeLow;
+    DWORD    dwReserved0;
+    DWORD    dwReserved1;
+    char  cFileName[MAX_PATH];
+    char  cAlternateFileName[14];
+} WIN32_FIND_DATAA;
+typedef struct _WIN32_FILE_ATTRIBUTE_DATA {
+    DWORD    dwFileAttributes;
+    FILETIME ftCreationTime;
+    FILETIME ftLastAccessTime;
+    FILETIME ftLastWriteTime;
+    DWORD    nFileSizeHigh;
+    DWORD    nFileSizeLow;
+} WIN32_FILE_ATTRIBUTE_DATA;
+typedef enum _GET_FILEEX_INFO_LEVELS {
+    GetFileExInfoStandard,
+    GetFileExMaxInfoLevel
+} GET_FILEEX_INFO_LEVELS;
+typedef struct _STARTUPINFOA {
+  DWORD  cb;
+  char * lpReserved;
+  char * lpDesktop;
+  char * lpTitle;
+  DWORD  dwX;
+  DWORD  dwY;
+  DWORD  dwXSize;
+  DWORD  dwYSize;
+  DWORD  dwXCountChars;
+  DWORD  dwYCountChars;
+  DWORD  dwFillAttribute;
+  DWORD  dwFlags;
+  WORD   wShowWindow;
+  WORD   cbReserved2;
+  char * lpReserved2;
+  HANDLE hStdInput;
+  HANDLE hStdOutput;
+  HANDLE hStdError;
+} STARTUPINFOA, *LPSTARTUPINFOA;
+typedef struct _PROCESS_INFORMATION {
+  HANDLE hProcess;
+  HANDLE hThread;
+  DWORD  dwProcessId;
+  DWORD  dwThreadId;
+} PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;
+
+#define INFINITE 0xffffffffl
+#define INVALID_HANDLE_VALUE ((void *)(intptr_t)(-1))
+#define STARTF_USESTDHANDLES 0x00000100
+
+typedef DWORD WINAPI THREAD_START_ROUTINE(void *parameter);
+
+GB_DLL_IMPORT DWORD   WINAPI GetLastError       (void);
+GB_DLL_IMPORT BOOL    WINAPI CloseHandle        (HANDLE object);
+GB_DLL_IMPORT HANDLE  WINAPI CreateSemaphoreA   (SECURITY_ATTRIBUTES *semaphore_attributes, LONG initial_count,
+                                                 LONG maximum_count, char const *name);
+GB_DLL_IMPORT BOOL    WINAPI ReleaseSemaphore   (HANDLE semaphore, LONG release_count, LONG *previous_count);
+GB_DLL_IMPORT DWORD   WINAPI WaitForSingleObject(HANDLE handle, DWORD milliseconds);
+GB_DLL_IMPORT HANDLE  WINAPI CreateThread       (SECURITY_ATTRIBUTES *semaphore_attributes, size_t stack_size,
+                                                 THREAD_START_ROUTINE *start_address, void *parameter,
+                                                 DWORD creation_flags, DWORD *thread_id);
+GB_DLL_IMPORT DWORD   WINAPI GetThreadId        (HANDLE handle);
+GB_DLL_IMPORT void    WINAPI RaiseException     (DWORD, DWORD, DWORD, ULONG_PTR const *);
+GB_DLL_IMPORT BOOL    WINAPI TerminateThread    (HANDLE hThread, DWORD dwExitCode);
+GB_DLL_IMPORT BOOL    WINAPI CreateProcessA     (char const * lpApplicationName, char * lpCommandLine,
+                                                 SECURITY_ATTRIBUTES* lpProcessAttrs, SECURITY_ATTRIBUTES* lpThreadAttributes,
+                                                 BOOL bInheritHandles, DWORD dwCreationFlags, void* lpEnvironment,
+                                                 char const * lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo,
+                                                 LPPROCESS_INFORMATION lpProcessInformation);
+GB_DLL_IMPORT BOOL    WINAPI GetExitCodeProcess (HANDLE hProcess, DWORD *lpExitCode);
+GB_DLL_IMPORT BOOL    WINAPI CreatePipe         (HANDLE *hReadPipe, HANDLE *hWritePipe, SECURITY_ATTRIBUTES* lpPipeAttributes,
+                                                 DWORD nSize);
+GB_DLL_IMPORT BOOL    WINAPI TerminateProcess   (HANDLE hProcess, UINT uExitCode);
+GB_DLL_IMPORT BOOL    WINAPI SetHandleInformation(HANDLE hObject, DWORD dwMask, DWORD dwFlags);
+
+uintptr_t _beginthreadex( // NATIVE CODE
+   void *security,
+   unsigned stack_size,
+   unsigned ( __stdcall *start_address )( void * ),
+   void *arglist,
+   unsigned initflag,
+   unsigned *thrdaddr
+);
+
+
+GB_DLL_IMPORT BOOL      WINAPI GetLogicalProcessorInformation(SYSTEM_LOGICAL_PROCESSOR_INFORMATION *buffer, DWORD *return_length);
+GB_DLL_IMPORT DWORD_PTR WINAPI SetThreadAffinityMask(HANDLE thread, DWORD_PTR check_mask);
+GB_DLL_IMPORT HANDLE    WINAPI GetCurrentThread(void);
+
+#define PAGE_NOACCESS          0x01
+#define PAGE_READONLY          0x02
+#define PAGE_READWRITE         0x04
+#define PAGE_WRITECOPY         0x08
+#define PAGE_EXECUTE           0x10
+#define PAGE_EXECUTE_READ      0x20
+#define PAGE_EXECUTE_READWRITE 0x40
+#define PAGE_EXECUTE_WRITECOPY 0x80
+#define PAGE_GUARD            0x100
+#define PAGE_NOCACHE          0x200
+#define PAGE_WRITECOMBINE     0x400
+
+#define MEM_COMMIT           0x1000
+#define MEM_RESERVE          0x2000
+#define MEM_DECOMMIT         0x4000
+#define MEM_RELEASE          0x8000
+#define MEM_FREE            0x10000
+#define MEM_PRIVATE         0x20000
+#define MEM_MAPPED          0x40000
+#define MEM_RESET           0x80000
+#define MEM_TOP_DOWN       0x100000
+#define MEM_LARGE_PAGES  0x20000000
+#define MEM_4MB_PAGES    0x80000000
+
+
+
+
+GB_DLL_IMPORT void * WINAPI VirtualAlloc (void *addr, size_t size, DWORD allocation_type, DWORD protect);
+GB_DLL_IMPORT size_t  WINAPI VirtualQuery (void const *address, MEMORY_BASIC_INFORMATION *buffer, size_t length);
+GB_DLL_IMPORT BOOL   WINAPI VirtualFree  (void *address, size_t size, DWORD free_type);
+GB_DLL_IMPORT void   WINAPI GetSystemInfo(SYSTEM_INFO *system_info);
+
+
+#define GENERIC_READ             0x80000000
+#define GENERIC_WRITE            0x40000000
+#define GENERIC_EXECUTE          0x20000000
+#define GENERIC_ALL              0x10000000
+#define FILE_SHARE_READ          0x00000001
+#define FILE_SHARE_WRITE         0x00000002
+#define FILE_SHARE_DELETE        0x00000004
+#define CREATE_NEW               1
+#define CREATE_ALWAYS            2
+#define OPEN_EXISTING            3
+#define OPEN_ALWAYS              4
+#define TRUNCATE_EXISTING        5
+#define FILE_ATTRIBUTE_READONLY  0x00000001
+#define FILE_ATTRIBUTE_NORMAL    0x00000080
+#define FILE_ATTRIBUTE_TEMPORARY 0x00000100
+#define FILE_ATTRIBUTE_DIRECTORY 0x00000010
+#define ERROR_FILE_NOT_FOUND     2l
+#define ERROR_ACCESS_DENIED      5L
+#define ERROR_NO_MORE_FILES      18l
+#define ERROR_FILE_EXISTS        80l
+#define ERROR_ALREADY_EXISTS     183l
+#define STD_INPUT_HANDLE         ((DWORD)-10)
+#define STD_OUTPUT_HANDLE        ((DWORD)-11)
+#define STD_ERROR_HANDLE         ((DWORD)-12)
+
+GB_DLL_IMPORT int           MultiByteToWideChar(UINT code_page, DWORD flags, char const *   multi_byte_str, int multi_byte_len, wchar_t const *wide_char_str,  int wide_char_len);
+GB_DLL_IMPORT int           WideCharToMultiByte(UINT code_page, DWORD flags, wchar_t const *wide_char_str,  int wide_char_len, char const *    multi_byte_str, int multi_byte_len);
+GB_DLL_IMPORT BOOL   WINAPI SetFilePointerEx(HANDLE file, LARGE_INTEGER distance_to_move,
+                                             LARGE_INTEGER *new_file_pointer, DWORD move_method);
+GB_DLL_IMPORT BOOL   WINAPI ReadFile        (HANDLE file, void *buffer, DWORD bytes_to_read, DWORD *bytes_read, OVERLAPPED *overlapped);
+GB_DLL_IMPORT BOOL   WINAPI WriteFile       (HANDLE file, void const *buffer, DWORD bytes_to_write, DWORD *bytes_written, OVERLAPPED *overlapped);
+GB_DLL_IMPORT HANDLE WINAPI CreateFileW     (wchar_t const *path, DWORD desired_access, DWORD share_mode,
+                                             SECURITY_ATTRIBUTES *, DWORD creation_disposition,
+                                             DWORD flags_and_attributes, HANDLE template_file);
+GB_DLL_IMPORT HANDLE WINAPI CreateFileA     (char const *path, DWORD desired_access, DWORD share_mode,
+                                             SECURITY_ATTRIBUTES *, DWORD creation_disposition,
+                                             DWORD flags_and_attributes, HANDLE template_file);
+GB_DLL_IMPORT HANDLE WINAPI GetStdHandle    (DWORD std_handle);
+GB_DLL_IMPORT BOOL   WINAPI GetFileSizeEx   (HANDLE file, LARGE_INTEGER *size);
+GB_DLL_IMPORT BOOL   WINAPI SetEndOfFile    (HANDLE file);
+GB_DLL_IMPORT HANDLE WINAPI FindFirstFileW  (wchar_t const *path, WIN32_FIND_DATAW *data);
+GB_DLL_IMPORT HANDLE WINAPI FindFirstFileA  (char const *path, WIN32_FIND_DATAA *data);
+GB_DLL_IMPORT BOOL   WINAPI FindNextFileA   (HANDLE find_find, WIN32_FIND_DATAA *data);
+GB_DLL_IMPORT BOOL   WINAPI FindClose       (HANDLE find_file);
+GB_DLL_IMPORT BOOL   WINAPI GetFileAttributesExW(wchar_t const *path, GET_FILEEX_INFO_LEVELS info_level_id, WIN32_FILE_ATTRIBUTE_DATA *data);
+GB_DLL_IMPORT BOOL   WINAPI CopyFileW(wchar_t const *old_f, wchar_t const *new_f, BOOL fail_if_exists);
+GB_DLL_IMPORT BOOL   WINAPI MoveFileW(wchar_t const *old_f, wchar_t const *new_f);
+GB_DLL_IMPORT BOOL   WINAPI DeleteFileA     (char const *path);
+GB_DLL_IMPORT BOOL   WINAPI CreateDirectoryA(char const *path);
+GB_DLL_IMPORT BOOL   WINAPI RemoveDirectoryA(char const *path);
+GB_DLL_IMPORT BOOL   WINAPI MoveFileA       (char const *old_path, char const *new_path);
+
+GB_DLL_IMPORT DWORD  WINAPI GetFullPathNameA(char const *lpFileName, DWORD nBufferLength, char *lpBuffer, char **lpFilePart);
+
+GB_DLL_IMPORT HMODULE WINAPI LoadLibraryA  (char const *filename);
+GB_DLL_IMPORT BOOL    WINAPI FreeLibrary   (HMODULE module);
+GB_DLL_IMPORT FARPROC WINAPI GetProcAddress(HMODULE module, char const *name);
+
+GB_DLL_IMPORT BOOL WINAPI QueryPerformanceFrequency(LARGE_INTEGER *frequency);
+GB_DLL_IMPORT BOOL WINAPI QueryPerformanceCounter  (LARGE_INTEGER *counter);
+GB_DLL_IMPORT void WINAPI GetSystemTimeAsFileTime  (FILETIME *system_time_as_file_time);
+GB_DLL_IMPORT void WINAPI Sleep(DWORD milliseconds);
+GB_DLL_IMPORT void WINAPI ExitProcess(UINT exit_code);
+
+GB_DLL_IMPORT BOOL  WINAPI SetEnvironmentVariableA(char const *name, char const *value);
+GB_DLL_IMPORT DWORD WINAPI GetEnvironmentVariableA(char const * lpName, char * lpBuffer, DWORD nSize);
+
+GB_DLL_IMPORT short WINAPI htons(short hostshort);
+GB_DLL_IMPORT int   WINAPI htonl(int   hostint);
+GB_DLL_IMPORT short WINAPI ntohs(short netshort);
+GB_DLL_IMPORT int   WINAPI ntohl(int   netint);
diff --git a/shared/include/stb_ds.h b/shared/include/stb_ds.h
new file mode 100644 (file)
index 0000000..e84c82d
--- /dev/null
@@ -0,0 +1,1895 @@
+/* stb_ds.h - v0.67 - public domain data structures - Sean Barrett 2019
+
+   This is a single-header-file library that provides easy-to-use
+   dynamic arrays and hash tables for C (also works in C++).
+
+   For a gentle introduction:
+      http://nothings.org/stb_ds
+
+   To use this library, do this in *one* C or C++ file:
+      #define STB_DS_IMPLEMENTATION
+      #include "stb_ds.h"
+
+TABLE OF CONTENTS
+
+  Table of Contents
+  Compile-time options
+  License
+  Documentation
+  Notes
+  Notes - Dynamic arrays
+  Notes - Hash maps
+  Credits
+
+COMPILE-TIME OPTIONS
+
+  #define STBDS_NO_SHORT_NAMES
+
+     This flag needs to be set globally.
+
+     By default stb_ds exposes shorter function names that are not qualified
+     with the "stbds_" prefix. If these names conflict with the names in your
+     code, define this flag.
+
+  #define STBDS_SIPHASH_2_4
+
+     This flag only needs to be set in the file containing #define STB_DS_IMPLEMENTATION.
+
+     By default stb_ds.h hashes using a weaker variant of SipHash and a custom hash for
+     4- and 8-byte keys. On 64-bit platforms, you can define the above flag to force
+     stb_ds.h to use specification-compliant SipHash-2-4 for all keys. Doing so makes
+     hash table insertion about 20% slower on 4- and 8-byte keys, 5% slower on
+     64-byte keys, and 10% slower on 256-byte keys on my test computer.
+
+  #define STBDS_REALLOC(context,ptr,size) better_realloc
+  #define STBDS_FREE(context,ptr)         better_free
+
+     These defines only need to be set in the file containing #define STB_DS_IMPLEMENTATION.
+
+     By default stb_ds uses stdlib realloc() and free() for memory management. You can
+     substitute your own functions instead by defining these symbols. You must either
+     define both, or neither. Note that at the moment, 'context' will always be NULL.
+     @TODO add an array/hash initialization function that takes a memory context pointer.
+
+  #define STBDS_UNIT_TESTS
+
+     Defines a function stbds_unit_tests() that checks the functioning of the data structures.
+
+  Note that on older versions of gcc (e.g. 5.x.x) you may need to build with '-std=c++0x'
+     (or equivalentally '-std=c++11') when using anonymous structures as seen on the web
+     page or in STBDS_UNIT_TESTS.
+
+LICENSE
+
+  Placed in the public domain and also MIT licensed.
+  See end of file for detailed license information.
+
+DOCUMENTATION
+
+  Dynamic Arrays
+
+    Non-function interface:
+
+      Declare an empty dynamic array of type T
+        T* foo = NULL;
+
+      Access the i'th item of a dynamic array 'foo' of type T, T* foo:
+        foo[i]
+
+    Functions (actually macros)
+
+      arrfree:
+        void arrfree(T*);
+          Frees the array.
+
+      arrlen:
+        ptrdiff_t arrlen(T*);
+          Returns the number of elements in the array.
+
+      arrlenu:
+        size_t arrlenu(T*);
+          Returns the number of elements in the array as an unsigned type.
+
+      arrpop:
+        T arrpop(T* a)
+          Removes the final element of the array and returns it.
+
+      arrput:
+        T arrput(T* a, T b);
+          Appends the item b to the end of array a. Returns b.
+
+      arrins:
+        T arrins(T* a, int p, T b);
+          Inserts the item b into the middle of array a, into a[p],
+          moving the rest of the array over. Returns b.
+
+      arrinsn:
+        void arrinsn(T* a, int p, int n);
+          Inserts n uninitialized items into array a starting at a[p],
+          moving the rest of the array over.
+
+      arraddnptr:
+        T* arraddnptr(T* a, int n)
+          Appends n uninitialized items onto array at the end.
+          Returns a pointer to the first uninitialized item added.
+
+      arraddnindex:
+        size_t arraddnindex(T* a, int n)
+          Appends n uninitialized items onto array at the end.
+          Returns the index of the first uninitialized item added.
+
+      arrdel:
+        void arrdel(T* a, int p);
+          Deletes the element at a[p], moving the rest of the array over.
+
+      arrdeln:
+        void arrdeln(T* a, int p, int n);
+          Deletes n elements starting at a[p], moving the rest of the array over.
+
+      arrdelswap:
+        void arrdelswap(T* a, int p);
+          Deletes the element at a[p], replacing it with the element from
+          the end of the array. O(1) performance.
+
+      arrsetlen:
+        void arrsetlen(T* a, int n);
+          Changes the length of the array to n. Allocates uninitialized
+          slots at the end if necessary.
+
+      arrsetcap:
+        size_t arrsetcap(T* a, int n);
+          Sets the length of allocated storage to at least n. It will not
+          change the length of the array.
+
+      arrcap:
+        size_t arrcap(T* a);
+          Returns the number of total elements the array can contain without
+          needing to be reallocated.
+
+  Hash maps & String hash maps
+
+    Given T is a structure type: struct { TK key; TV value; }. Note that some
+    functions do not require TV value and can have other fields. For string
+    hash maps, TK must be 'char *'.
+
+    Special interface:
+
+      stbds_rand_seed:
+        void stbds_rand_seed(size_t seed);
+          For security against adversarially chosen data, you should seed the
+          library with a strong random number. Or at least seed it with time().
+
+      stbds_hash_string:
+        size_t stbds_hash_string(char *str, size_t seed);
+          Returns a hash value for a string.
+
+      stbds_hash_bytes:
+        size_t stbds_hash_bytes(void *p, size_t len, size_t seed);
+          These functions hash an arbitrary number of bytes. The function
+          uses a custom hash for 4- and 8-byte data, and a weakened version
+          of SipHash for everything else. On 64-bit platforms you can get
+          specification-compliant SipHash-2-4 on all data by defining
+          STBDS_SIPHASH_2_4, at a significant cost in speed.
+
+    Non-function interface:
+
+      Declare an empty hash map of type T
+        T* foo = NULL;
+
+      Access the i'th entry in a hash table T* foo:
+        foo[i]
+
+    Function interface (actually macros):
+
+      hmfree
+      shfree
+        void hmfree(T*);
+        void shfree(T*);
+          Frees the hashmap and sets the pointer to NULL.
+
+      hmlen
+      shlen
+        ptrdiff_t hmlen(T*)
+        ptrdiff_t shlen(T*)
+          Returns the number of elements in the hashmap.
+
+      hmlenu
+      shlenu
+        size_t hmlenu(T*)
+        size_t shlenu(T*)
+          Returns the number of elements in the hashmap.
+
+      hmgeti
+      shgeti
+      hmgeti_ts
+        ptrdiff_t hmgeti(T*, TK key)
+        ptrdiff_t shgeti(T*, char* key)
+        ptrdiff_t hmgeti_ts(T*, TK key, ptrdiff_t tempvar)
+          Returns the index in the hashmap which has the key 'key', or -1
+          if the key is not present.
+
+      hmget
+      hmget_ts
+      shget
+        TV hmget(T*, TK key)
+        TV shget(T*, char* key)
+        TV hmget_ts(T*, TK key, ptrdiff_t tempvar)
+          Returns the value corresponding to 'key' in the hashmap.
+          The structure must have a 'value' field
+
+      hmgets
+      shgets
+        T hmgets(T*, TK key)
+        T shgets(T*, char* key)
+          Returns the structure corresponding to 'key' in the hashmap.
+
+      hmgetp
+      shgetp
+      hmgetp_ts
+      hmgetp_null
+      shgetp_null
+        T* hmgetp(T*, TK key)
+        T* shgetp(T*, char* key)
+        T* hmgetp_ts(T*, TK key, ptrdiff_t tempvar)
+        T* hmgetp_null(T*, TK key)
+        T* shgetp_null(T*, char *key)
+          Returns a pointer to the structure corresponding to 'key' in
+          the hashmap. Functions ending in "_null" return NULL if the key
+          is not present in the hashmap; the others return a pointer to a
+          structure holding the default value (but not the searched-for key).
+
+      hmdefault
+      shdefault
+        TV hmdefault(T*, TV value)
+        TV shdefault(T*, TV value)
+          Sets the default value for the hashmap, the value which will be
+          returned by hmget/shget if the key is not present.
+
+      hmdefaults
+      shdefaults
+        TV hmdefaults(T*, T item)
+        TV shdefaults(T*, T item)
+          Sets the default struct for the hashmap, the contents which will be
+          returned by hmgets/shgets if the key is not present.
+
+      hmput
+      shput
+        TV hmput(T*, TK key, TV value)
+        TV shput(T*, char* key, TV value)
+          Inserts a <key,value> pair into the hashmap. If the key is already
+          present in the hashmap, updates its value.
+
+      hmputs
+      shputs
+        T hmputs(T*, T item)
+        T shputs(T*, T item)
+          Inserts a struct with T.key into the hashmap. If the struct is already
+          present in the hashmap, updates it.
+
+      hmdel
+      shdel
+        int hmdel(T*, TK key)
+        int shdel(T*, char* key)
+          If 'key' is in the hashmap, deletes its entry and returns 1.
+          Otherwise returns 0.
+
+    Function interface (actually macros) for strings only:
+
+      sh_new_strdup
+        void sh_new_strdup(T*);
+          Overwrites the existing pointer with a newly allocated
+          string hashmap which will automatically allocate and free
+          each string key using realloc/free
+
+      sh_new_arena
+        void sh_new_arena(T*);
+          Overwrites the existing pointer with a newly allocated
+          string hashmap which will automatically allocate each string
+          key to a string arena. Every string key ever used by this
+          hash table remains in the arena until the arena is freed.
+          Additionally, any key which is deleted and reinserted will
+          be allocated multiple times in the string arena.
+
+NOTES
+
+  * These data structures are realloc'd when they grow, and the macro
+    "functions" write to the provided pointer. This means: (a) the pointer
+    must be an lvalue, and (b) the pointer to the data structure is not
+    stable, and you must maintain it the same as you would a realloc'd
+    pointer. For example, if you pass a pointer to a dynamic array to a
+    function which updates it, the function must return back the new
+    pointer to the caller. This is the price of trying to do this in C.
+
+  * The following are the only functions that are thread-safe on a single data
+    structure, i.e. can be run in multiple threads simultaneously on the same
+    data structure
+        hmlen        shlen
+        hmlenu       shlenu
+        hmget_ts     shget_ts
+        hmgeti_ts    shgeti_ts
+        hmgets_ts    shgets_ts
+
+  * You iterate over the contents of a dynamic array and a hashmap in exactly
+    the same way, using arrlen/hmlen/shlen:
+
+      for (i=0; i < arrlen(foo); ++i)
+         ... foo[i] ...
+
+  * All operations except arrins/arrdel are O(1) amortized, but individual
+    operations can be slow, so these data structures may not be suitable
+    for real time use. Dynamic arrays double in capacity as needed, so
+    elements are copied an average of once. Hash tables double/halve
+    their size as needed, with appropriate hysteresis to maintain O(1)
+    performance.
+
+NOTES - DYNAMIC ARRAY
+
+  * If you know how long a dynamic array is going to be in advance, you can avoid
+    extra memory allocations by using arrsetlen to allocate it to that length in
+    advance and use foo[n] while filling it out, or arrsetcap to allocate the memory
+    for that length and use arrput/arrpush as normal.
+
+  * Unlike some other versions of the dynamic array, this version should
+    be safe to use with strict-aliasing optimizations.
+
+NOTES - HASH MAP
+
+  * For compilers other than GCC and clang (e.g. Visual Studio), for hmput/hmget/hmdel
+    and variants, the key must be an lvalue (so the macro can take the address of it).
+    Extensions are used that eliminate this requirement if you're using C99 and later
+    in GCC or clang, or if you're using C++ in GCC. But note that this can make your
+    code less portable.
+
+  * To test for presence of a key in a hashmap, just do 'hmgeti(foo,key) >= 0'.
+
+  * The iteration order of your data in the hashmap is determined solely by the
+    order of insertions and deletions. In particular, if you never delete, new
+    keys are always added at the end of the array. This will be consistent
+    across all platforms and versions of the library. However, you should not
+    attempt to serialize the internal hash table, as the hash is not consistent
+    between different platforms, and may change with future versions of the library.
+
+  * Use sh_new_arena() for string hashmaps that you never delete from. Initialize
+    with NULL if you're managing the memory for your strings, or your strings are
+    never freed (at least until the hashmap is freed). Otherwise, use sh_new_strdup().
+    @TODO: make an arena variant that garbage collects the strings with a trivial
+    copy collector into a new arena whenever the table shrinks / rebuilds. Since
+    current arena recommendation is to only use arena if it never deletes, then
+    this can just replace current arena implementation.
+
+  * If adversarial input is a serious concern and you're on a 64-bit platform,
+    enable STBDS_SIPHASH_2_4 (see the 'Compile-time options' section), and pass
+    a strong random number to stbds_rand_seed.
+
+  * The default value for the hash table is stored in foo[-1], so if you
+    use code like 'hmget(T,k)->value = 5' you can accidentally overwrite
+    the value stored by hmdefault if 'k' is not present.
+
+CREDITS
+
+  Sean Barrett -- library, idea for dynamic array API/implementation
+  Per Vognsen  -- idea for hash table API/implementation
+  Rafael Sachetto -- arrpop()
+  github:HeroicKatora -- arraddn() reworking
+
+  Bugfixes:
+    Andy Durdin
+    Shane Liesegang
+    Vinh Truong
+    Andreas Molzer
+    github:hashitaku
+    github:srdjanstipic
+    Macoy Madson
+    Andreas Vennstrom
+    Tobias Mansfield-Williams
+*/
+
+#ifdef STBDS_UNIT_TESTS
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#ifndef INCLUDE_STB_DS_H
+#define INCLUDE_STB_DS_H
+
+#include <stddef.h>
+#include <string.h>
+
+#ifndef STBDS_NO_SHORT_NAMES
+#define arrlen      stbds_arrlen
+#define arrlenu     stbds_arrlenu
+#define arrput      stbds_arrput
+#define arrpush     stbds_arrput
+#define arrpop      stbds_arrpop
+#define arrfree     stbds_arrfree
+#define arraddn     stbds_arraddn // deprecated, use one of the following instead:
+#define arraddnptr  stbds_arraddnptr
+#define arraddnindex stbds_arraddnindex
+#define arrsetlen   stbds_arrsetlen
+#define arrlast     stbds_arrlast
+#define arrins      stbds_arrins
+#define arrinsn     stbds_arrinsn
+#define arrdel      stbds_arrdel
+#define arrdeln     stbds_arrdeln
+#define arrdelswap  stbds_arrdelswap
+#define arrcap      stbds_arrcap
+#define arrsetcap   stbds_arrsetcap
+
+#define hmput       stbds_hmput
+#define hmputs      stbds_hmputs
+#define hmget       stbds_hmget
+#define hmget_ts    stbds_hmget_ts
+#define hmgets      stbds_hmgets
+#define hmgetp      stbds_hmgetp
+#define hmgetp_ts   stbds_hmgetp_ts
+#define hmgetp_null stbds_hmgetp_null
+#define hmgeti      stbds_hmgeti
+#define hmgeti_ts   stbds_hmgeti_ts
+#define hmdel       stbds_hmdel
+#define hmlen       stbds_hmlen
+#define hmlenu      stbds_hmlenu
+#define hmfree      stbds_hmfree
+#define hmdefault   stbds_hmdefault
+#define hmdefaults  stbds_hmdefaults
+
+#define shput       stbds_shput
+#define shputi      stbds_shputi
+#define shputs      stbds_shputs
+#define shget       stbds_shget
+#define shgeti      stbds_shgeti
+#define shgets      stbds_shgets
+#define shgetp      stbds_shgetp
+#define shgetp_null stbds_shgetp_null
+#define shdel       stbds_shdel
+#define shlen       stbds_shlen
+#define shlenu      stbds_shlenu
+#define shfree      stbds_shfree
+#define shdefault   stbds_shdefault
+#define shdefaults  stbds_shdefaults
+#define sh_new_arena  stbds_sh_new_arena
+#define sh_new_strdup stbds_sh_new_strdup
+
+#define stralloc    stbds_stralloc
+#define strreset    stbds_strreset
+#endif
+
+#if defined(STBDS_REALLOC) && !defined(STBDS_FREE) || !defined(STBDS_REALLOC) && defined(STBDS_FREE)
+#error "You must define both STBDS_REALLOC and STBDS_FREE, or neither."
+#endif
+#if !defined(STBDS_REALLOC) && !defined(STBDS_FREE)
+#include <stdlib.h>
+#define STBDS_REALLOC(c,p,s) realloc(p,s)
+#define STBDS_FREE(c,p)      free(p)
+#endif
+
+#ifdef _MSC_VER
+#define STBDS_NOTUSED(v)  (void)(v)
+#else
+#define STBDS_NOTUSED(v)  (void)sizeof(v)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// for security against attackers, seed the library with a random number, at least time() but stronger is better
+extern void stbds_rand_seed(size_t seed);
+
+// these are the hash functions used internally if you want to test them or use them for other purposes
+extern size_t stbds_hash_bytes(void *p, size_t len, size_t seed);
+extern size_t stbds_hash_string(char *str, size_t seed);
+
+// this is a simple string arena allocator, initialize with e.g. 'stbds_string_arena my_arena={0}'.
+typedef struct stbds_string_arena stbds_string_arena;
+extern char * stbds_stralloc(stbds_string_arena *a, char *str);
+extern void   stbds_strreset(stbds_string_arena *a);
+
+// have to #define STBDS_UNIT_TESTS to call this
+extern void stbds_unit_tests(void);
+
+///////////////
+//
+// Everything below here is implementation details
+//
+
+extern void * stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap);
+extern void   stbds_arrfreef(void *a);
+extern void   stbds_hmfree_func(void *p, size_t elemsize);
+extern void * stbds_hmget_key(void *a, size_t elemsize, void *key, size_t keysize, int mode);
+extern void * stbds_hmget_key_ts(void *a, size_t elemsize, void *key, size_t keysize, ptrdiff_t *temp, int mode);
+extern void * stbds_hmput_default(void *a, size_t elemsize);
+extern void * stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, int mode);
+extern void * stbds_hmdel_key(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode);
+extern void * stbds_shmode_func(size_t elemsize, int mode);
+
+#ifdef __cplusplus
+}
+#endif
+
+#if defined(__GNUC__) || defined(__clang__)
+#define STBDS_HAS_TYPEOF
+#ifdef __cplusplus
+//#define STBDS_HAS_LITERAL_ARRAY  // this is currently broken for clang
+#endif
+#endif
+
+#if !defined(__cplusplus)
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+#define STBDS_HAS_LITERAL_ARRAY
+#endif
+#endif
+
+// this macro takes the address of the argument, but on gcc/clang can accept rvalues
+#if defined(STBDS_HAS_LITERAL_ARRAY) && defined(STBDS_HAS_TYPEOF)
+  #if __clang__
+  #define STBDS_ADDRESSOF(typevar, value)     ((__typeof__(typevar)[1]){value}) // literal array decays to pointer to value
+  #else
+  #define STBDS_ADDRESSOF(typevar, value)     ((typeof(typevar)[1]){value}) // literal array decays to pointer to value
+  #endif
+#else
+#define STBDS_ADDRESSOF(typevar, value)     &(value)
+#endif
+
+#define STBDS_OFFSETOF(var,field)           ((char *) &(var)->field - (char *) (var))
+
+#define stbds_header(t)  ((stbds_array_header *) (t) - 1)
+#define stbds_temp(t)    stbds_header(t)->temp
+#define stbds_temp_key(t) (*(char **) stbds_header(t)->hash_table)
+
+#define stbds_arrsetcap(a,n)   (stbds_arrgrow(a,0,n))
+#define stbds_arrsetlen(a,n)   ((stbds_arrcap(a) < (size_t) (n) ? stbds_arrsetcap((a),(size_t)(n)),0 : 0), (a) ? stbds_header(a)->length = (size_t) (n) : 0)
+#define stbds_arrcap(a)        ((a) ? stbds_header(a)->capacity : 0)
+#define stbds_arrlen(a)        ((a) ? (ptrdiff_t) stbds_header(a)->length : 0)
+#define stbds_arrlenu(a)       ((a) ?             stbds_header(a)->length : 0)
+#define stbds_arrput(a,v)      (stbds_arrmaybegrow(a,1), (a)[stbds_header(a)->length++] = (v))
+#define stbds_arrpush          stbds_arrput  // synonym
+#define stbds_arrpop(a)        (stbds_header(a)->length--, (a)[stbds_header(a)->length])
+#define stbds_arraddn(a,n)     ((void)(stbds_arraddnindex(a, n)))    // deprecated, use one of the following instead:
+#define stbds_arraddnptr(a,n)  (stbds_arrmaybegrow(a,n), (n) ? (stbds_header(a)->length += (n), &(a)[stbds_header(a)->length-(n)]) : (a))
+#define stbds_arraddnindex(a,n)(stbds_arrmaybegrow(a,n), (n) ? (stbds_header(a)->length += (n), stbds_header(a)->length-(n)) : stbds_arrlen(a))
+#define stbds_arraddnoff       stbds_arraddnindex
+#define stbds_arrlast(a)       ((a)[stbds_header(a)->length-1])
+#define stbds_arrfree(a)       ((void) ((a) ? STBDS_FREE(NULL,stbds_header(a)) : (void)0), (a)=NULL)
+#define stbds_arrdel(a,i)      stbds_arrdeln(a,i,1)
+#define stbds_arrdeln(a,i,n)   (memmove(&(a)[i], &(a)[(i)+(n)], sizeof *(a) * (stbds_header(a)->length-(n)-(i))), stbds_header(a)->length -= (n))
+#define stbds_arrdelswap(a,i)  ((a)[i] = stbds_arrlast(a), stbds_header(a)->length -= 1)
+#define stbds_arrinsn(a,i,n)   (stbds_arraddn((a),(n)), memmove(&(a)[(i)+(n)], &(a)[i], sizeof *(a) * (stbds_header(a)->length-(n)-(i))))
+#define stbds_arrins(a,i,v)    (stbds_arrinsn((a),(i),1), (a)[i]=(v))
+
+#define stbds_arrmaybegrow(a,n)  ((!(a) || stbds_header(a)->length + (n) > stbds_header(a)->capacity) \
+                                  ? (stbds_arrgrow(a,n,0),0) : 0)
+
+#define stbds_arrgrow(a,b,c)   ((a) = stbds_arrgrowf_wrapper((a), sizeof *(a), (b), (c)))
+
+#define stbds_hmput(t, k, v) \
+    ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, 0),   \
+     (t)[stbds_temp((t)-1)].key = (k),    \
+     (t)[stbds_temp((t)-1)].value = (v))
+
+#define stbds_hmputs(t, s) \
+    ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), &(s).key, sizeof (s).key, STBDS_HM_BINARY), \
+     (t)[stbds_temp((t)-1)] = (s))
+
+#define stbds_hmgeti(t,k) \
+    ((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, STBDS_HM_BINARY), \
+      stbds_temp((t)-1))
+
+#define stbds_hmgeti_ts(t,k,temp) \
+    ((t) = stbds_hmget_key_ts_wrapper((t), sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, &(temp), STBDS_HM_BINARY), \
+      (temp))
+
+#define stbds_hmgetp(t, k) \
+    ((void) stbds_hmgeti(t,k), &(t)[stbds_temp((t)-1)])
+
+#define stbds_hmgetp_ts(t, k, temp) \
+    ((void) stbds_hmgeti_ts(t,k,temp), &(t)[temp])
+
+#define stbds_hmdel(t,k) \
+    (((t) = stbds_hmdel_key_wrapper((t),sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, STBDS_OFFSETOF((t),key), STBDS_HM_BINARY)),(t)?stbds_temp((t)-1):0)
+
+#define stbds_hmdefault(t, v) \
+    ((t) = stbds_hmput_default_wrapper((t), sizeof *(t)), (t)[-1].value = (v))
+
+#define stbds_hmdefaults(t, s) \
+    ((t) = stbds_hmput_default_wrapper((t), sizeof *(t)), (t)[-1] = (s))
+
+#define stbds_hmfree(p)        \
+    ((void) ((p) != NULL ? stbds_hmfree_func((p)-1,sizeof*(p)),0 : 0),(p)=NULL)
+
+#define stbds_hmgets(t, k)    (*stbds_hmgetp(t,k))
+#define stbds_hmget(t, k)     (stbds_hmgetp(t,k)->value)
+#define stbds_hmget_ts(t, k, temp)  (stbds_hmgetp_ts(t,k,temp)->value)
+#define stbds_hmlen(t)        ((t) ? (ptrdiff_t) stbds_header((t)-1)->length-1 : 0)
+#define stbds_hmlenu(t)       ((t) ?             stbds_header((t)-1)->length-1 : 0)
+#define stbds_hmgetp_null(t,k)  (stbds_hmgeti(t,k) == -1 ? NULL : &(t)[stbds_temp((t)-1)])
+
+#define stbds_shput(t, k, v) \
+    ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING),   \
+     (t)[stbds_temp((t)-1)].value = (v))
+
+#define stbds_shputi(t, k, v) \
+    ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING),   \
+     (t)[stbds_temp((t)-1)].value = (v), stbds_temp((t)-1))
+
+#define stbds_shputs(t, s) \
+    ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (s).key, sizeof (s).key, STBDS_HM_STRING), \
+     (t)[stbds_temp((t)-1)] = (s), \
+     (t)[stbds_temp((t)-1)].key = stbds_temp_key((t)-1)) // above line overwrites whole structure, so must rewrite key here if it was allocated internally
+
+#define stbds_pshput(t, p) \
+    ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (p)->key, sizeof (p)->key, STBDS_HM_PTR_TO_STRING), \
+     (t)[stbds_temp((t)-1)] = (p))
+
+#define stbds_shgeti(t,k) \
+     ((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING), \
+      stbds_temp((t)-1))
+
+#define stbds_pshgeti(t,k) \
+     ((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (*(t))->key, STBDS_HM_PTR_TO_STRING), \
+      stbds_temp((t)-1))
+
+#define stbds_shgetp(t, k) \
+    ((void) stbds_shgeti(t,k), &(t)[stbds_temp((t)-1)])
+
+#define stbds_pshget(t, k) \
+    ((void) stbds_pshgeti(t,k), (t)[stbds_temp((t)-1)])
+
+#define stbds_shdel(t,k) \
+    (((t) = stbds_hmdel_key_wrapper((t),sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_OFFSETOF((t),key), STBDS_HM_STRING)),(t)?stbds_temp((t)-1):0)
+#define stbds_pshdel(t,k) \
+    (((t) = stbds_hmdel_key_wrapper((t),sizeof *(t), (void*) (k), sizeof (*(t))->key, STBDS_OFFSETOF(*(t),key), STBDS_HM_PTR_TO_STRING)),(t)?stbds_temp((t)-1):0)
+
+#define stbds_sh_new_arena(t)  \
+    ((t) = stbds_shmode_func_wrapper(t, sizeof *(t), STBDS_SH_ARENA))
+#define stbds_sh_new_strdup(t) \
+    ((t) = stbds_shmode_func_wrapper(t, sizeof *(t), STBDS_SH_STRDUP))
+
+#define stbds_shdefault(t, v)  stbds_hmdefault(t,v)
+#define stbds_shdefaults(t, s) stbds_hmdefaults(t,s)
+
+#define stbds_shfree       stbds_hmfree
+#define stbds_shlenu       stbds_hmlenu
+
+#define stbds_shgets(t, k) (*stbds_shgetp(t,k))
+#define stbds_shget(t, k)  (stbds_shgetp(t,k)->value)
+#define stbds_shgetp_null(t,k)  (stbds_shgeti(t,k) == -1 ? NULL : &(t)[stbds_temp((t)-1)])
+#define stbds_shlen        stbds_hmlen
+
+typedef struct
+{
+  size_t      length;
+  size_t      capacity;
+  void      * hash_table;
+  ptrdiff_t   temp;
+} stbds_array_header;
+
+typedef struct stbds_string_block
+{
+  struct stbds_string_block *next;
+  char storage[8];
+} stbds_string_block;
+
+struct stbds_string_arena
+{
+  stbds_string_block *storage;
+  size_t remaining;
+  unsigned char block;
+  unsigned char mode;  // this isn't used by the string arena itself
+};
+
+#define STBDS_HM_BINARY         0
+#define STBDS_HM_STRING         1
+
+enum
+{
+   STBDS_SH_NONE,
+   STBDS_SH_DEFAULT,
+   STBDS_SH_STRDUP,
+   STBDS_SH_ARENA
+};
+
+#ifdef __cplusplus
+// in C we use implicit assignment from these void*-returning functions to T*.
+// in C++ these templates make the same code work
+template<class T> static T * stbds_arrgrowf_wrapper(T *a, size_t elemsize, size_t addlen, size_t min_cap) {
+  return (T*)stbds_arrgrowf((void *)a, elemsize, addlen, min_cap);
+}
+template<class T> static T * stbds_hmget_key_wrapper(T *a, size_t elemsize, void *key, size_t keysize, int mode) {
+  return (T*)stbds_hmget_key((void*)a, elemsize, key, keysize, mode);
+}
+template<class T> static T * stbds_hmget_key_ts_wrapper(T *a, size_t elemsize, void *key, size_t keysize, ptrdiff_t *temp, int mode) {
+  return (T*)stbds_hmget_key_ts((void*)a, elemsize, key, keysize, temp, mode);
+}
+template<class T> static T * stbds_hmput_default_wrapper(T *a, size_t elemsize) {
+  return (T*)stbds_hmput_default((void *)a, elemsize);
+}
+template<class T> static T * stbds_hmput_key_wrapper(T *a, size_t elemsize, void *key, size_t keysize, int mode) {
+  return (T*)stbds_hmput_key((void*)a, elemsize, key, keysize, mode);
+}
+template<class T> static T * stbds_hmdel_key_wrapper(T *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode){
+  return (T*)stbds_hmdel_key((void*)a, elemsize, key, keysize, keyoffset, mode);
+}
+template<class T> static T * stbds_shmode_func_wrapper(T *, size_t elemsize, int mode) {
+  return (T*)stbds_shmode_func(elemsize, mode);
+}
+#else
+#define stbds_arrgrowf_wrapper            stbds_arrgrowf
+#define stbds_hmget_key_wrapper           stbds_hmget_key
+#define stbds_hmget_key_ts_wrapper        stbds_hmget_key_ts
+#define stbds_hmput_default_wrapper       stbds_hmput_default
+#define stbds_hmput_key_wrapper           stbds_hmput_key
+#define stbds_hmdel_key_wrapper           stbds_hmdel_key
+#define stbds_shmode_func_wrapper(t,e,m)  stbds_shmode_func(e,m)
+#endif
+
+#endif // INCLUDE_STB_DS_H
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+//   IMPLEMENTATION
+//
+
+#ifdef STB_DS_IMPLEMENTATION
+#include <assert.h>
+#include <string.h>
+
+#ifndef STBDS_ASSERT
+#define STBDS_ASSERT_WAS_UNDEFINED
+#define STBDS_ASSERT(x)   ((void) 0)
+#endif
+
+#ifdef STBDS_STATISTICS
+#define STBDS_STATS(x)   x
+size_t stbds_array_grow;
+size_t stbds_hash_grow;
+size_t stbds_hash_shrink;
+size_t stbds_hash_rebuild;
+size_t stbds_hash_probes;
+size_t stbds_hash_alloc;
+size_t stbds_rehash_probes;
+size_t stbds_rehash_items;
+#else
+#define STBDS_STATS(x)
+#endif
+
+//
+// stbds_arr implementation
+//
+
+//int *prev_allocs[65536];
+//int num_prev;
+
+void *stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap)
+{
+  stbds_array_header temp={0}; // force debugging
+  void *b;
+  size_t min_len = stbds_arrlen(a) + addlen;
+  (void) sizeof(temp);
+
+  // compute the minimum capacity needed
+  if (min_len > min_cap)
+    min_cap = min_len;
+
+  if (min_cap <= stbds_arrcap(a))
+    return a;
+
+  // increase needed capacity to guarantee O(1) amortized
+  if (min_cap < 2 * stbds_arrcap(a))
+    min_cap = 2 * stbds_arrcap(a);
+  else if (min_cap < 4)
+    min_cap = 4;
+
+  //if (num_prev < 65536) if (a) prev_allocs[num_prev++] = (int *) ((char *) a+1);
+  //if (num_prev == 2201)
+  //  num_prev = num_prev;
+  b = STBDS_REALLOC(NULL, (a) ? stbds_header(a) : 0, elemsize * min_cap + sizeof(stbds_array_header));
+  //if (num_prev < 65536) prev_allocs[num_prev++] = (int *) (char *) b;
+  b = (char *) b + sizeof(stbds_array_header);
+  if (a == NULL) {
+    stbds_header(b)->length = 0;
+    stbds_header(b)->hash_table = 0;
+    stbds_header(b)->temp = 0;
+  } else {
+    STBDS_STATS(++stbds_array_grow);
+  }
+  stbds_header(b)->capacity = min_cap;
+
+  return b;
+}
+
+void stbds_arrfreef(void *a)
+{
+  STBDS_FREE(NULL, stbds_header(a));
+}
+
+//
+// stbds_hm hash table implementation
+//
+
+#ifdef STBDS_INTERNAL_SMALL_BUCKET
+#define STBDS_BUCKET_LENGTH      4
+#else
+#define STBDS_BUCKET_LENGTH      8
+#endif
+
+#define STBDS_BUCKET_SHIFT      (STBDS_BUCKET_LENGTH == 8 ? 3 : 2)
+#define STBDS_BUCKET_MASK       (STBDS_BUCKET_LENGTH-1)
+#define STBDS_CACHE_LINE_SIZE   64
+
+#define STBDS_ALIGN_FWD(n,a)   (((n) + (a) - 1) & ~((a)-1))
+
+typedef struct
+{
+   size_t    hash [STBDS_BUCKET_LENGTH];
+   ptrdiff_t index[STBDS_BUCKET_LENGTH];
+} stbds_hash_bucket; // in 32-bit, this is one 64-byte cache line; in 64-bit, each array is one 64-byte cache line
+
+typedef struct
+{
+  char * temp_key; // this MUST be the first field of the hash table
+  size_t slot_count;
+  size_t used_count;
+  size_t used_count_threshold;
+  size_t used_count_shrink_threshold;
+  size_t tombstone_count;
+  size_t tombstone_count_threshold;
+  size_t seed;
+  size_t slot_count_log2;
+  stbds_string_arena string;
+  stbds_hash_bucket *storage; // not a separate allocation, just 64-byte aligned storage after this struct
+} stbds_hash_index;
+
+#define STBDS_INDEX_EMPTY    -1
+#define STBDS_INDEX_DELETED  -2
+#define STBDS_INDEX_IN_USE(x)  ((x) >= 0)
+
+#define STBDS_HASH_EMPTY      0
+#define STBDS_HASH_DELETED    1
+
+static size_t stbds_hash_seed=0x31415926;
+
+void stbds_rand_seed(size_t seed)
+{
+  stbds_hash_seed = seed;
+}
+
+#define stbds_load_32_or_64(var, temp, v32, v64_hi, v64_lo)                                          \
+  temp = v64_lo ^ v32, temp <<= 16, temp <<= 16, temp >>= 16, temp >>= 16, /* discard if 32-bit */   \
+  var = v64_hi, var <<= 16, var <<= 16,                                    /* discard if 32-bit */   \
+  var ^= temp ^ v32
+
+#define STBDS_SIZE_T_BITS           ((sizeof (size_t)) * 8)
+
+static size_t stbds_probe_position(size_t hash, size_t slot_count, size_t slot_log2)
+{
+  size_t pos;
+  STBDS_NOTUSED(slot_log2);
+  pos = hash & (slot_count-1);
+  #ifdef STBDS_INTERNAL_BUCKET_START
+  pos &= ~STBDS_BUCKET_MASK;
+  #endif
+  return pos;
+}
+
+static size_t stbds_log2(size_t slot_count)
+{
+  size_t n=0;
+  while (slot_count > 1) {
+    slot_count >>= 1;
+    ++n;
+  }
+  return n;
+}
+
+static stbds_hash_index *stbds_make_hash_index(size_t slot_count, stbds_hash_index *ot)
+{
+  stbds_hash_index *t;
+  t = (stbds_hash_index *) STBDS_REALLOC(NULL,0,(slot_count >> STBDS_BUCKET_SHIFT) * sizeof(stbds_hash_bucket) + sizeof(stbds_hash_index) + STBDS_CACHE_LINE_SIZE-1);
+  t->storage = (stbds_hash_bucket *) STBDS_ALIGN_FWD((size_t) (t+1), STBDS_CACHE_LINE_SIZE);
+  t->slot_count = slot_count;
+  t->slot_count_log2 = stbds_log2(slot_count);
+  t->tombstone_count = 0;
+  t->used_count = 0;
+
+  #if 0 // A1
+  t->used_count_threshold        = slot_count*12/16; // if 12/16th of table is occupied, grow
+  t->tombstone_count_threshold   = slot_count* 2/16; // if tombstones are 2/16th of table, rebuild
+  t->used_count_shrink_threshold = slot_count* 4/16; // if table is only 4/16th full, shrink
+  #elif 1 // A2
+  //t->used_count_threshold        = slot_count*12/16; // if 12/16th of table is occupied, grow
+  //t->tombstone_count_threshold   = slot_count* 3/16; // if tombstones are 3/16th of table, rebuild
+  //t->used_count_shrink_threshold = slot_count* 4/16; // if table is only 4/16th full, shrink
+
+  // compute without overflowing
+  t->used_count_threshold        = slot_count - (slot_count>>2);
+  t->tombstone_count_threshold   = (slot_count>>3) + (slot_count>>4);
+  t->used_count_shrink_threshold = slot_count >> 2;
+
+  #elif 0 // B1
+  t->used_count_threshold        = slot_count*13/16; // if 13/16th of table is occupied, grow
+  t->tombstone_count_threshold   = slot_count* 2/16; // if tombstones are 2/16th of table, rebuild
+  t->used_count_shrink_threshold = slot_count* 5/16; // if table is only 5/16th full, shrink
+  #else // C1
+  t->used_count_threshold        = slot_count*14/16; // if 14/16th of table is occupied, grow
+  t->tombstone_count_threshold   = slot_count* 2/16; // if tombstones are 2/16th of table, rebuild
+  t->used_count_shrink_threshold = slot_count* 6/16; // if table is only 6/16th full, shrink
+  #endif
+  // Following statistics were measured on a Core i7-6700 @ 4.00Ghz, compiled with clang 7.0.1 -O2
+    // Note that the larger tables have high variance as they were run fewer times
+  //     A1            A2          B1           C1
+  //    0.10ms :     0.10ms :     0.10ms :     0.11ms :      2,000 inserts creating 2K table
+  //    0.96ms :     0.95ms :     0.97ms :     1.04ms :     20,000 inserts creating 20K table
+  //   14.48ms :    14.46ms :    10.63ms :    11.00ms :    200,000 inserts creating 200K table
+  //  195.74ms :   196.35ms :   203.69ms :   214.92ms :  2,000,000 inserts creating 2M table
+  // 2193.88ms :  2209.22ms :  2285.54ms :  2437.17ms : 20,000,000 inserts creating 20M table
+  //   65.27ms :    53.77ms :    65.33ms :    65.47ms : 500,000 inserts & deletes in 2K table
+  //   72.78ms :    62.45ms :    71.95ms :    72.85ms : 500,000 inserts & deletes in 20K table
+  //   89.47ms :    77.72ms :    96.49ms :    96.75ms : 500,000 inserts & deletes in 200K table
+  //   97.58ms :    98.14ms :    97.18ms :    97.53ms : 500,000 inserts & deletes in 2M table
+  //  118.61ms :   119.62ms :   120.16ms :   118.86ms : 500,000 inserts & deletes in 20M table
+  //  192.11ms :   194.39ms :   196.38ms :   195.73ms : 500,000 inserts & deletes in 200M table
+
+  if (slot_count <= STBDS_BUCKET_LENGTH)
+    t->used_count_shrink_threshold = 0;
+  // to avoid infinite loop, we need to guarantee that at least one slot is empty and will terminate probes
+  STBDS_ASSERT(t->used_count_threshold + t->tombstone_count_threshold < t->slot_count);
+  STBDS_STATS(++stbds_hash_alloc);
+  if (ot) {
+    t->string = ot->string;
+    // reuse old seed so we can reuse old hashes so below "copy out old data" doesn't do any hashing
+    t->seed = ot->seed;
+  } else {
+    size_t a,b,temp;
+    memset(&t->string, 0, sizeof(t->string));
+    t->seed = stbds_hash_seed;
+    // LCG
+    // in 32-bit, a =          2147001325   b =  715136305
+    // in 64-bit, a = 2862933555777941757   b = 3037000493
+    stbds_load_32_or_64(a,temp, 2147001325, 0x27bb2ee6, 0x87b0b0fd);
+    stbds_load_32_or_64(b,temp,  715136305,          0, 0xb504f32d);
+    stbds_hash_seed = stbds_hash_seed  * a + b;
+  }
+
+  {
+    size_t i,j;
+    for (i=0; i < slot_count >> STBDS_BUCKET_SHIFT; ++i) {
+      stbds_hash_bucket *b = &t->storage[i];
+      for (j=0; j < STBDS_BUCKET_LENGTH; ++j)
+        b->hash[j] = STBDS_HASH_EMPTY;
+      for (j=0; j < STBDS_BUCKET_LENGTH; ++j)
+        b->index[j] = STBDS_INDEX_EMPTY;
+    }
+  }
+
+  // copy out the old data, if any
+  if (ot) {
+    size_t i,j;
+    t->used_count = ot->used_count;
+    for (i=0; i < ot->slot_count >> STBDS_BUCKET_SHIFT; ++i) {
+      stbds_hash_bucket *ob = &ot->storage[i];
+      for (j=0; j < STBDS_BUCKET_LENGTH; ++j) {
+        if (STBDS_INDEX_IN_USE(ob->index[j])) {
+          size_t hash = ob->hash[j];
+          size_t pos = stbds_probe_position(hash, t->slot_count, t->slot_count_log2);
+          size_t step = STBDS_BUCKET_LENGTH;
+          STBDS_STATS(++stbds_rehash_items);
+          for (;;) {
+            size_t limit,z;
+            stbds_hash_bucket *bucket;
+            bucket = &t->storage[pos >> STBDS_BUCKET_SHIFT];
+            STBDS_STATS(++stbds_rehash_probes);
+
+            for (z=pos & STBDS_BUCKET_MASK; z < STBDS_BUCKET_LENGTH; ++z) {
+              if (bucket->hash[z] == 0) {
+                bucket->hash[z] = hash;
+                bucket->index[z] = ob->index[j];
+                goto done;
+              }
+            }
+
+            limit = pos & STBDS_BUCKET_MASK;
+            for (z = 0; z < limit; ++z) {
+              if (bucket->hash[z] == 0) {
+                bucket->hash[z] = hash;
+                bucket->index[z] = ob->index[j];
+                goto done;
+              }
+            }
+
+            pos += step;                  // quadratic probing
+            step += STBDS_BUCKET_LENGTH;
+            pos &= (t->slot_count-1);
+          }
+        }
+       done:
+        ;
+      }
+    }
+  }
+
+  return t;
+}
+
+#define STBDS_ROTATE_LEFT(val, n)   (((val) << (n)) | ((val) >> (STBDS_SIZE_T_BITS - (n))))
+#define STBDS_ROTATE_RIGHT(val, n)  (((val) >> (n)) | ((val) << (STBDS_SIZE_T_BITS - (n))))
+
+size_t stbds_hash_string(char *str, size_t seed)
+{
+  size_t hash = seed;
+  while (*str)
+     hash = STBDS_ROTATE_LEFT(hash, 9) + (unsigned char) *str++;
+
+  // Thomas Wang 64-to-32 bit mix function, hopefully also works in 32 bits
+  hash ^= seed;
+  hash = (~hash) + (hash << 18);
+  hash ^= hash ^ STBDS_ROTATE_RIGHT(hash,31);
+  hash = hash * 21;
+  hash ^= hash ^ STBDS_ROTATE_RIGHT(hash,11);
+  hash += (hash << 6);
+  hash ^= STBDS_ROTATE_RIGHT(hash,22);
+  return hash+seed;
+}
+
+#ifdef STBDS_SIPHASH_2_4
+#define STBDS_SIPHASH_C_ROUNDS 2
+#define STBDS_SIPHASH_D_ROUNDS 4
+typedef int STBDS_SIPHASH_2_4_can_only_be_used_in_64_bit_builds[sizeof(size_t) == 8 ? 1 : -1];
+#endif
+
+#ifndef STBDS_SIPHASH_C_ROUNDS
+#define STBDS_SIPHASH_C_ROUNDS 1
+#endif
+#ifndef STBDS_SIPHASH_D_ROUNDS
+#define STBDS_SIPHASH_D_ROUNDS 1
+#endif
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4127) // conditional expression is constant, for do..while(0) and sizeof()==
+#endif
+
+static size_t stbds_siphash_bytes(void *p, size_t len, size_t seed)
+{
+  unsigned char *d = (unsigned char *) p;
+  size_t i,j;
+  size_t v0,v1,v2,v3, data;
+
+  // hash that works on 32- or 64-bit registers without knowing which we have
+  // (computes different results on 32-bit and 64-bit platform)
+  // derived from siphash, but on 32-bit platforms very different as it uses 4 32-bit state not 4 64-bit
+  v0 = ((((size_t) 0x736f6d65 << 16) << 16) + 0x70736575) ^  seed;
+  v1 = ((((size_t) 0x646f7261 << 16) << 16) + 0x6e646f6d) ^ ~seed;
+  v2 = ((((size_t) 0x6c796765 << 16) << 16) + 0x6e657261) ^  seed;
+  v3 = ((((size_t) 0x74656462 << 16) << 16) + 0x79746573) ^ ~seed;
+
+  #ifdef STBDS_TEST_SIPHASH_2_4
+  // hardcoded with key material in the siphash test vectors
+  v0 ^= 0x0706050403020100ull ^  seed;
+  v1 ^= 0x0f0e0d0c0b0a0908ull ^ ~seed;
+  v2 ^= 0x0706050403020100ull ^  seed;
+  v3 ^= 0x0f0e0d0c0b0a0908ull ^ ~seed;
+  #endif
+
+  #define STBDS_SIPROUND() \
+    do {                   \
+      v0 += v1; v1 = STBDS_ROTATE_LEFT(v1, 13);  v1 ^= v0; v0 = STBDS_ROTATE_LEFT(v0,STBDS_SIZE_T_BITS/2); \
+      v2 += v3; v3 = STBDS_ROTATE_LEFT(v3, 16);  v3 ^= v2;                                                 \
+      v2 += v1; v1 = STBDS_ROTATE_LEFT(v1, 17);  v1 ^= v2; v2 = STBDS_ROTATE_LEFT(v2,STBDS_SIZE_T_BITS/2); \
+      v0 += v3; v3 = STBDS_ROTATE_LEFT(v3, 21);  v3 ^= v0;                                                 \
+    } while (0)
+
+  for (i=0; i+sizeof(size_t) <= len; i += sizeof(size_t), d += sizeof(size_t)) {
+    data = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);
+    data |= (size_t) (d[4] | (d[5] << 8) | (d[6] << 16) | (d[7] << 24)) << 16 << 16; // discarded if size_t == 4
+
+    v3 ^= data;
+    for (j=0; j < STBDS_SIPHASH_C_ROUNDS; ++j)
+      STBDS_SIPROUND();
+    v0 ^= data;
+  }
+  data = len << (STBDS_SIZE_T_BITS-8);
+  switch (len - i) {
+    case 7: data |= ((size_t) d[6] << 24) << 24; // fall through
+    case 6: data |= ((size_t) d[5] << 20) << 20; // fall through
+    case 5: data |= ((size_t) d[4] << 16) << 16; // fall through
+    case 4: data |= (d[3] << 24); // fall through
+    case 3: data |= (d[2] << 16); // fall through
+    case 2: data |= (d[1] << 8); // fall through
+    case 1: data |= d[0]; // fall through
+    case 0: break;
+  }
+  v3 ^= data;
+  for (j=0; j < STBDS_SIPHASH_C_ROUNDS; ++j)
+    STBDS_SIPROUND();
+  v0 ^= data;
+  v2 ^= 0xff;
+  for (j=0; j < STBDS_SIPHASH_D_ROUNDS; ++j)
+    STBDS_SIPROUND();
+
+#ifdef STBDS_SIPHASH_2_4
+  return v0^v1^v2^v3;
+#else
+  return v1^v2^v3; // slightly stronger since v0^v3 in above cancels out final round operation? I tweeted at the authors of SipHash about this but they didn't reply
+#endif
+}
+
+size_t stbds_hash_bytes(void *p, size_t len, size_t seed)
+{
+#ifdef STBDS_SIPHASH_2_4
+  return stbds_siphash_bytes(p,len,seed);
+#else
+  unsigned char *d = (unsigned char *) p;
+
+  if (len == 4) {
+    unsigned int hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);
+    #if 0
+    // HASH32-A  Bob Jenkin's hash function w/o large constants
+    hash ^= seed;
+    hash -= (hash<<6);
+    hash ^= (hash>>17);
+    hash -= (hash<<9);
+    hash ^= seed;
+    hash ^= (hash<<4);
+    hash -= (hash<<3);
+    hash ^= (hash<<10);
+    hash ^= (hash>>15);
+    #elif 1
+    // HASH32-BB  Bob Jenkin's presumably-accidental version of Thomas Wang hash with rotates turned into shifts.
+    // Note that converting these back to rotates makes it run a lot slower, presumably due to collisions, so I'm
+    // not really sure what's going on.
+    hash ^= seed;
+    hash = (hash ^ 61) ^ (hash >> 16);
+    hash = hash + (hash << 3);
+    hash = hash ^ (hash >> 4);
+    hash = hash * 0x27d4eb2d;
+    hash ^= seed;
+    hash = hash ^ (hash >> 15);
+    #else  // HASH32-C   -  Murmur3
+    hash ^= seed;
+    hash *= 0xcc9e2d51;
+    hash = (hash << 17) | (hash >> 15);
+    hash *= 0x1b873593;
+    hash ^= seed;
+    hash = (hash << 19) | (hash >> 13);
+    hash = hash*5 + 0xe6546b64;
+    hash ^= hash >> 16;
+    hash *= 0x85ebca6b;
+    hash ^= seed;
+    hash ^= hash >> 13;
+    hash *= 0xc2b2ae35;
+    hash ^= hash >> 16;
+    #endif
+    // Following statistics were measured on a Core i7-6700 @ 4.00Ghz, compiled with clang 7.0.1 -O2
+    // Note that the larger tables have high variance as they were run fewer times
+    //  HASH32-A   //  HASH32-BB  //  HASH32-C
+    //    0.10ms   //    0.10ms   //    0.10ms :      2,000 inserts creating 2K table
+    //    0.96ms   //    0.95ms   //    0.99ms :     20,000 inserts creating 20K table
+    //   14.69ms   //   14.43ms   //   14.97ms :    200,000 inserts creating 200K table
+    //  199.99ms   //  195.36ms   //  202.05ms :  2,000,000 inserts creating 2M table
+    // 2234.84ms   // 2187.74ms   // 2240.38ms : 20,000,000 inserts creating 20M table
+    //   55.68ms   //   53.72ms   //   57.31ms : 500,000 inserts & deletes in 2K table
+    //   63.43ms   //   61.99ms   //   65.73ms : 500,000 inserts & deletes in 20K table
+    //   80.04ms   //   77.96ms   //   81.83ms : 500,000 inserts & deletes in 200K table
+    //  100.42ms   //   97.40ms   //  102.39ms : 500,000 inserts & deletes in 2M table
+    //  119.71ms   //  120.59ms   //  121.63ms : 500,000 inserts & deletes in 20M table
+    //  185.28ms   //  195.15ms   //  187.74ms : 500,000 inserts & deletes in 200M table
+    //   15.58ms   //   14.79ms   //   15.52ms : 200,000 inserts creating 200K table with varying key spacing
+
+    return (((size_t) hash << 16 << 16) | hash) ^ seed;
+  } else if (len == 8 && sizeof(size_t) == 8) {
+    size_t hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);
+    hash |= (size_t) (d[4] | (d[5] << 8) | (d[6] << 16) | (d[7] << 24)) << 16 << 16; // avoid warning if size_t == 4
+    hash ^= seed;
+    hash = (~hash) + (hash << 21);
+    hash ^= STBDS_ROTATE_RIGHT(hash,24);
+    hash *= 265;
+    hash ^= STBDS_ROTATE_RIGHT(hash,14);
+    hash ^= seed;
+    hash *= 21;
+    hash ^= STBDS_ROTATE_RIGHT(hash,28);
+    hash += (hash << 31);
+    hash = (~hash) + (hash << 18);
+    return hash;
+  } else {
+    return stbds_siphash_bytes(p,len,seed);
+  }
+#endif
+}
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+
+static int stbds_is_key_equal(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode, size_t i)
+{
+  if (mode >= STBDS_HM_STRING)
+    return 0==strcmp((char *) key, * (char **) ((char *) a + elemsize*i + keyoffset));
+  else
+    return 0==memcmp(key, (char *) a + elemsize*i + keyoffset, keysize);
+}
+
+#define STBDS_HASH_TO_ARR(x,elemsize) ((char*) (x) - (elemsize))
+#define STBDS_ARR_TO_HASH(x,elemsize) ((char*) (x) + (elemsize))
+
+#define stbds_hash_table(a)  ((stbds_hash_index *) stbds_header(a)->hash_table)
+
+void stbds_hmfree_func(void *a, size_t elemsize)
+{
+  if (a == NULL) return;
+  if (stbds_hash_table(a) != NULL) {
+    if (stbds_hash_table(a)->string.mode == STBDS_SH_STRDUP) {
+      size_t i;
+      // skip 0th element, which is default
+      for (i=1; i < stbds_header(a)->length; ++i)
+        STBDS_FREE(NULL, *(char**) ((char *) a + elemsize*i));
+    }
+    stbds_strreset(&stbds_hash_table(a)->string);
+  }
+  STBDS_FREE(NULL, stbds_header(a)->hash_table);
+  STBDS_FREE(NULL, stbds_header(a));
+}
+
+static ptrdiff_t stbds_hm_find_slot(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode)
+{
+  void *raw_a = STBDS_HASH_TO_ARR(a,elemsize);
+  stbds_hash_index *table = stbds_hash_table(raw_a);
+  size_t hash = mode >= STBDS_HM_STRING ? stbds_hash_string((char*)key,table->seed) : stbds_hash_bytes(key, keysize,table->seed);
+  size_t step = STBDS_BUCKET_LENGTH;
+  size_t limit,i;
+  size_t pos;
+  stbds_hash_bucket *bucket;
+
+  if (hash < 2) hash += 2; // stored hash values are forbidden from being 0, so we can detect empty slots
+
+  pos = stbds_probe_position(hash, table->slot_count, table->slot_count_log2);
+
+  for (;;) {
+    STBDS_STATS(++stbds_hash_probes);
+    bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT];
+
+    // start searching from pos to end of bucket, this should help performance on small hash tables that fit in cache
+    for (i=pos & STBDS_BUCKET_MASK; i < STBDS_BUCKET_LENGTH; ++i) {
+      if (bucket->hash[i] == hash) {
+        if (stbds_is_key_equal(a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) {
+          return (pos & ~STBDS_BUCKET_MASK)+i;
+        }
+      } else if (bucket->hash[i] == STBDS_HASH_EMPTY) {
+        return -1;
+      }
+    }
+
+    // search from beginning of bucket to pos
+    limit = pos & STBDS_BUCKET_MASK;
+    for (i = 0; i < limit; ++i) {
+      if (bucket->hash[i] == hash) {
+        if (stbds_is_key_equal(a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) {
+          return (pos & ~STBDS_BUCKET_MASK)+i;
+        }
+      } else if (bucket->hash[i] == STBDS_HASH_EMPTY) {
+        return -1;
+      }
+    }
+
+    // quadratic probing
+    pos += step;
+    step += STBDS_BUCKET_LENGTH;
+    pos &= (table->slot_count-1);
+  }
+  /* NOTREACHED */
+}
+
+void * stbds_hmget_key_ts(void *a, size_t elemsize, void *key, size_t keysize, ptrdiff_t *temp, int mode)
+{
+  size_t keyoffset = 0;
+  if (a == NULL) {
+    // make it non-empty so we can return a temp
+    a = stbds_arrgrowf(0, elemsize, 0, 1);
+    stbds_header(a)->length += 1;
+    memset(a, 0, elemsize);
+    *temp = STBDS_INDEX_EMPTY;
+    // adjust a to point after the default element
+    return STBDS_ARR_TO_HASH(a,elemsize);
+  } else {
+    stbds_hash_index *table;
+    void *raw_a = STBDS_HASH_TO_ARR(a,elemsize);
+    // adjust a to point to the default element
+    table = (stbds_hash_index *) stbds_header(raw_a)->hash_table;
+    if (table == 0) {
+      *temp = -1;
+    } else {
+      ptrdiff_t slot = stbds_hm_find_slot(a, elemsize, key, keysize, keyoffset, mode);
+      if (slot < 0) {
+        *temp = STBDS_INDEX_EMPTY;
+      } else {
+        stbds_hash_bucket *b = &table->storage[slot >> STBDS_BUCKET_SHIFT];
+        *temp = b->index[slot & STBDS_BUCKET_MASK];
+      }
+    }
+    return a;
+  }
+}
+
+void * stbds_hmget_key(void *a, size_t elemsize, void *key, size_t keysize, int mode)
+{
+  ptrdiff_t temp;
+  void *p = stbds_hmget_key_ts(a, elemsize, key, keysize, &temp, mode);
+  stbds_temp(STBDS_HASH_TO_ARR(p,elemsize)) = temp;
+  return p;
+}
+
+void * stbds_hmput_default(void *a, size_t elemsize)
+{
+  // three cases:
+  //   a is NULL <- allocate
+  //   a has a hash table but no entries, because of shmode <- grow
+  //   a has entries <- do nothing
+  if (a == NULL || stbds_header(STBDS_HASH_TO_ARR(a,elemsize))->length == 0) {
+    a = stbds_arrgrowf(a ? STBDS_HASH_TO_ARR(a,elemsize) : NULL, elemsize, 0, 1);
+    stbds_header(a)->length += 1;
+    memset(a, 0, elemsize);
+    a=STBDS_ARR_TO_HASH(a,elemsize);
+  }
+  return a;
+}
+
+static char *stbds_strdup(char *str);
+
+void *stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, int mode)
+{
+  size_t keyoffset=0;
+  void *raw_a;
+  stbds_hash_index *table;
+
+  if (a == NULL) {
+    a = stbds_arrgrowf(0, elemsize, 0, 1);
+    memset(a, 0, elemsize);
+    stbds_header(a)->length += 1;
+    // adjust a to point AFTER the default element
+    a = STBDS_ARR_TO_HASH(a,elemsize);
+  }
+
+  // adjust a to point to the default element
+  raw_a = a;
+  a = STBDS_HASH_TO_ARR(a,elemsize);
+
+  table = (stbds_hash_index *) stbds_header(a)->hash_table;
+
+  if (table == NULL || table->used_count >= table->used_count_threshold) {
+    stbds_hash_index *nt;
+    size_t slot_count;
+
+    slot_count = (table == NULL) ? STBDS_BUCKET_LENGTH : table->slot_count*2;
+    nt = stbds_make_hash_index(slot_count, table);
+    if (table)
+      STBDS_FREE(NULL, table);
+    else
+      nt->string.mode = mode >= STBDS_HM_STRING ? STBDS_SH_DEFAULT : 0;
+    stbds_header(a)->hash_table = table = nt;
+    STBDS_STATS(++stbds_hash_grow);
+  }
+
+  // we iterate hash table explicitly because we want to track if we saw a tombstone
+  {
+    size_t hash = mode >= STBDS_HM_STRING ? stbds_hash_string((char*)key,table->seed) : stbds_hash_bytes(key, keysize,table->seed);
+    size_t step = STBDS_BUCKET_LENGTH;
+    size_t pos;
+    ptrdiff_t tombstone = -1;
+    stbds_hash_bucket *bucket;
+
+    // stored hash values are forbidden from being 0, so we can detect empty slots to early out quickly
+    if (hash < 2) hash += 2;
+
+    pos = stbds_probe_position(hash, table->slot_count, table->slot_count_log2);
+
+    for (;;) {
+      size_t limit, i;
+      STBDS_STATS(++stbds_hash_probes);
+      bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT];
+
+      // start searching from pos to end of bucket
+      for (i=pos & STBDS_BUCKET_MASK; i < STBDS_BUCKET_LENGTH; ++i) {
+        if (bucket->hash[i] == hash) {
+          if (stbds_is_key_equal(raw_a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) {
+            stbds_temp(a) = bucket->index[i];
+            if (mode >= STBDS_HM_STRING)
+              stbds_temp_key(a) = * (char **) ((char *) raw_a + elemsize*bucket->index[i] + keyoffset);
+            return STBDS_ARR_TO_HASH(a,elemsize);
+          }
+        } else if (bucket->hash[i] == 0) {
+          pos = (pos & ~STBDS_BUCKET_MASK) + i;
+          goto found_empty_slot;
+        } else if (tombstone < 0) {
+          if (bucket->index[i] == STBDS_INDEX_DELETED)
+            tombstone = (ptrdiff_t) ((pos & ~STBDS_BUCKET_MASK) + i);
+        }
+      }
+
+      // search from beginning of bucket to pos
+      limit = pos & STBDS_BUCKET_MASK;
+      for (i = 0; i < limit; ++i) {
+        if (bucket->hash[i] == hash) {
+          if (stbds_is_key_equal(raw_a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) {
+            stbds_temp(a) = bucket->index[i];
+            return STBDS_ARR_TO_HASH(a,elemsize);
+          }
+        } else if (bucket->hash[i] == 0) {
+          pos = (pos & ~STBDS_BUCKET_MASK) + i;
+          goto found_empty_slot;
+        } else if (tombstone < 0) {
+          if (bucket->index[i] == STBDS_INDEX_DELETED)
+            tombstone = (ptrdiff_t) ((pos & ~STBDS_BUCKET_MASK) + i);
+        }
+      }
+
+      // quadratic probing
+      pos += step;
+      step += STBDS_BUCKET_LENGTH;
+      pos &= (table->slot_count-1);
+    }
+   found_empty_slot:
+    if (tombstone >= 0) {
+      pos = tombstone;
+      --table->tombstone_count;
+    }
+    ++table->used_count;
+
+    {
+      ptrdiff_t i = (ptrdiff_t) stbds_arrlen(a);
+      // we want to do stbds_arraddn(1), but we can't use the macros since we don't have something of the right type
+      if ((size_t) i+1 > stbds_arrcap(a))
+        *(void **) &a = stbds_arrgrowf(a, elemsize, 1, 0);
+      raw_a = STBDS_ARR_TO_HASH(a,elemsize);
+
+      STBDS_ASSERT((size_t) i+1 <= stbds_arrcap(a));
+      stbds_header(a)->length = i+1;
+      bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT];
+      bucket->hash[pos & STBDS_BUCKET_MASK] = hash;
+      bucket->index[pos & STBDS_BUCKET_MASK] = i-1;
+      stbds_temp(a) = i-1;
+
+      switch (table->string.mode) {
+         case STBDS_SH_STRDUP:  stbds_temp_key(a) = *(char **) ((char *) a + elemsize*i) = stbds_strdup((char*) key); break;
+         case STBDS_SH_ARENA:   stbds_temp_key(a) = *(char **) ((char *) a + elemsize*i) = stbds_stralloc(&table->string, (char*)key); break;
+         case STBDS_SH_DEFAULT: stbds_temp_key(a) = *(char **) ((char *) a + elemsize*i) = (char *) key; break;
+         default:                memcpy((char *) a + elemsize*i, key, keysize); break;
+      }
+    }
+    return STBDS_ARR_TO_HASH(a,elemsize);
+  }
+}
+
+void * stbds_shmode_func(size_t elemsize, int mode)
+{
+  void *a = stbds_arrgrowf(0, elemsize, 0, 1);
+  stbds_hash_index *h;
+  memset(a, 0, elemsize);
+  stbds_header(a)->length = 1;
+  stbds_header(a)->hash_table = h = (stbds_hash_index *) stbds_make_hash_index(STBDS_BUCKET_LENGTH, NULL);
+  h->string.mode = (unsigned char) mode;
+  return STBDS_ARR_TO_HASH(a,elemsize);
+}
+
+void * stbds_hmdel_key(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode)
+{
+  if (a == NULL) {
+    return 0;
+  } else {
+    stbds_hash_index *table;
+    void *raw_a = STBDS_HASH_TO_ARR(a,elemsize);
+    table = (stbds_hash_index *) stbds_header(raw_a)->hash_table;
+    stbds_temp(raw_a) = 0;
+    if (table == 0) {
+      return a;
+    } else {
+      ptrdiff_t slot;
+      slot = stbds_hm_find_slot(a, elemsize, key, keysize, keyoffset, mode);
+      if (slot < 0)
+        return a;
+      else {
+        stbds_hash_bucket *b = &table->storage[slot >> STBDS_BUCKET_SHIFT];
+        int i = slot & STBDS_BUCKET_MASK;
+        ptrdiff_t old_index = b->index[i];
+        ptrdiff_t final_index = (ptrdiff_t) stbds_arrlen(raw_a)-1-1; // minus one for the raw_a vs a, and minus one for 'last'
+        STBDS_ASSERT(slot < (ptrdiff_t) table->slot_count);
+        --table->used_count;
+        ++table->tombstone_count;
+        stbds_temp(raw_a) = 1;
+        STBDS_ASSERT(table->used_count >= 0);
+        //STBDS_ASSERT(table->tombstone_count < table->slot_count/4);
+        b->hash[i] = STBDS_HASH_DELETED;
+        b->index[i] = STBDS_INDEX_DELETED;
+
+        if (mode == STBDS_HM_STRING && table->string.mode == STBDS_SH_STRDUP)
+          STBDS_FREE(NULL, *(char**) ((char *) a+elemsize*old_index));
+
+        // if indices are the same, memcpy is a no-op, but back-pointer-fixup will fail, so skip
+        if (old_index != final_index) {
+          // swap delete
+          memmove((char*) a + elemsize*old_index, (char*) a + elemsize*final_index, elemsize);
+
+          // now find the slot for the last element
+          if (mode == STBDS_HM_STRING)
+            slot = stbds_hm_find_slot(a, elemsize, *(char**) ((char *) a+elemsize*old_index + keyoffset), keysize, keyoffset, mode);
+          else
+            slot = stbds_hm_find_slot(a, elemsize,  (char* ) a+elemsize*old_index + keyoffset, keysize, keyoffset, mode);
+          STBDS_ASSERT(slot >= 0);
+          b = &table->storage[slot >> STBDS_BUCKET_SHIFT];
+          i = slot & STBDS_BUCKET_MASK;
+          STBDS_ASSERT(b->index[i] == final_index);
+          b->index[i] = old_index;
+        }
+        stbds_header(raw_a)->length -= 1;
+
+        if (table->used_count < table->used_count_shrink_threshold && table->slot_count > STBDS_BUCKET_LENGTH) {
+          stbds_header(raw_a)->hash_table = stbds_make_hash_index(table->slot_count>>1, table);
+          STBDS_FREE(NULL, table);
+          STBDS_STATS(++stbds_hash_shrink);
+        } else if (table->tombstone_count > table->tombstone_count_threshold) {
+          stbds_header(raw_a)->hash_table = stbds_make_hash_index(table->slot_count   , table);
+          STBDS_FREE(NULL, table);
+          STBDS_STATS(++stbds_hash_rebuild);
+        }
+
+        return a;
+      }
+    }
+  }
+  /* NOTREACHED */
+}
+
+static char *stbds_strdup(char *str)
+{
+  // to keep replaceable allocator simple, we don't want to use strdup.
+  // rolling our own also avoids problem of strdup vs _strdup
+  size_t len = strlen(str)+1;
+  char *p = (char*) STBDS_REALLOC(NULL, 0, len);
+  memmove(p, str, len);
+  return p;
+}
+
+#ifndef STBDS_STRING_ARENA_BLOCKSIZE_MIN
+#define STBDS_STRING_ARENA_BLOCKSIZE_MIN  512u
+#endif
+#ifndef STBDS_STRING_ARENA_BLOCKSIZE_MAX
+#define STBDS_STRING_ARENA_BLOCKSIZE_MAX  (1u<<20)
+#endif
+
+char *stbds_stralloc(stbds_string_arena *a, char *str)
+{
+  char *p;
+  size_t len = strlen(str)+1;
+  if (len > a->remaining) {
+    // compute the next blocksize
+    size_t blocksize = a->block;
+
+    // size is 512, 512, 1024, 1024, 2048, 2048, 4096, 4096, etc., so that
+    // there are log(SIZE) allocations to free when we destroy the table
+    blocksize = (size_t) (STBDS_STRING_ARENA_BLOCKSIZE_MIN) << (blocksize>>1);
+
+    // if size is under 1M, advance to next blocktype
+    if (blocksize < (size_t)(STBDS_STRING_ARENA_BLOCKSIZE_MAX))
+      ++a->block;
+
+    if (len > blocksize) {
+      // if string is larger than blocksize, then just allocate the full size.
+      // note that we still advance string_block so block size will continue
+      // increasing, so e.g. if somebody only calls this with 1000-long strings,
+      // eventually the arena will start doubling and handling those as well
+      stbds_string_block *sb = (stbds_string_block *) STBDS_REALLOC(NULL, 0, sizeof(*sb)-8 + len);
+      memmove(sb->storage, str, len);
+      if (a->storage) {
+        // insert it after the first element, so that we don't waste the space there
+        sb->next = a->storage->next;
+        a->storage->next = sb;
+      } else {
+        sb->next = 0;
+        a->storage = sb;
+        a->remaining = 0; // this is redundant, but good for clarity
+      }
+      return sb->storage;
+    } else {
+      stbds_string_block *sb = (stbds_string_block *) STBDS_REALLOC(NULL, 0, sizeof(*sb)-8 + blocksize);
+      sb->next = a->storage;
+      a->storage = sb;
+      a->remaining = blocksize;
+    }
+  }
+
+  STBDS_ASSERT(len <= a->remaining);
+  p = a->storage->storage + a->remaining - len;
+  a->remaining -= len;
+  memmove(p, str, len);
+  return p;
+}
+
+void stbds_strreset(stbds_string_arena *a)
+{
+  stbds_string_block *x,*y;
+  x = a->storage;
+  while (x) {
+    y = x->next;
+    STBDS_FREE(NULL, x);
+    x = y;
+  }
+  memset(a, 0, sizeof(*a));
+}
+
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+//   UNIT TESTS
+//
+
+#ifdef STBDS_UNIT_TESTS
+#include <stdio.h>
+#ifdef STBDS_ASSERT_WAS_UNDEFINED
+#undef STBDS_ASSERT
+#endif
+#ifndef STBDS_ASSERT
+#define STBDS_ASSERT assert
+#include <assert.h>
+#endif
+
+typedef struct { int key,b,c,d; } stbds_struct;
+typedef struct { int key[2],b,c,d; } stbds_struct2;
+
+static char buffer[256];
+char *strkey(int n)
+{
+#if defined(_WIN32) && defined(__STDC_WANT_SECURE_LIB__)
+   sprintf_s(buffer, sizeof(buffer), "test_%d", n);
+#else
+   sprintf(buffer, "test_%d", n);
+#endif
+   return buffer;
+}
+
+void stbds_unit_tests(void)
+{
+#if defined(_MSC_VER) && _MSC_VER <= 1200 && defined(__cplusplus)
+  // VC6 C++ doesn't like the template<> trick on unnamed structures, so do nothing!
+  STBDS_ASSERT(0);
+#else
+  const int testsize = 100000;
+  const int testsize2 = testsize/20;
+  int *arr=NULL;
+  struct { int   key;        int value; }  *intmap  = NULL;
+  struct { char *key;        int value; }  *strmap  = NULL, s;
+  struct { stbds_struct key; int value; }  *map     = NULL;
+  stbds_struct                             *map2    = NULL;
+  stbds_struct2                            *map3    = NULL;
+  stbds_string_arena                        sa      = { 0 };
+  int key3[2] = { 1,2 };
+  ptrdiff_t temp;
+
+  int i,j;
+
+  STBDS_ASSERT(arrlen(arr)==0);
+  for (i=0; i < 20000; i += 50) {
+    for (j=0; j < i; ++j)
+      arrpush(arr,j);
+    arrfree(arr);
+  }
+
+  for (i=0; i < 4; ++i) {
+    arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4);
+    arrdel(arr,i);
+    arrfree(arr);
+    arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4);
+    arrdelswap(arr,i);
+    arrfree(arr);
+  }
+
+  for (i=0; i < 5; ++i) {
+    arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4);
+    stbds_arrins(arr,i,5);
+    STBDS_ASSERT(arr[i] == 5);
+    if (i < 4)
+      STBDS_ASSERT(arr[4] == 4);
+    arrfree(arr);
+  }
+
+  i = 1;
+  STBDS_ASSERT(hmgeti(intmap,i) == -1);
+  hmdefault(intmap, -2);
+  STBDS_ASSERT(hmgeti(intmap, i) == -1);
+  STBDS_ASSERT(hmget (intmap, i) == -2);
+  for (i=0; i < testsize; i+=2)
+    hmput(intmap, i, i*5);
+  for (i=0; i < testsize; i+=1) {
+    if (i & 1) STBDS_ASSERT(hmget(intmap, i) == -2 );
+    else       STBDS_ASSERT(hmget(intmap, i) == i*5);
+    if (i & 1) STBDS_ASSERT(hmget_ts(intmap, i, temp) == -2 );
+    else       STBDS_ASSERT(hmget_ts(intmap, i, temp) == i*5);
+  }
+  for (i=0; i < testsize; i+=2)
+    hmput(intmap, i, i*3);
+  for (i=0; i < testsize; i+=1)
+    if (i & 1) STBDS_ASSERT(hmget(intmap, i) == -2 );
+    else       STBDS_ASSERT(hmget(intmap, i) == i*3);
+  for (i=2; i < testsize; i+=4)
+    hmdel(intmap, i); // delete half the entries
+  for (i=0; i < testsize; i+=1)
+    if (i & 3) STBDS_ASSERT(hmget(intmap, i) == -2 );
+    else       STBDS_ASSERT(hmget(intmap, i) == i*3);
+  for (i=0; i < testsize; i+=1)
+    hmdel(intmap, i); // delete the rest of the entries
+  for (i=0; i < testsize; i+=1)
+    STBDS_ASSERT(hmget(intmap, i) == -2 );
+  hmfree(intmap);
+  for (i=0; i < testsize; i+=2)
+    hmput(intmap, i, i*3);
+  hmfree(intmap);
+
+  #if defined(__clang__) || defined(__GNUC__)
+  #ifndef __cplusplus
+  intmap = NULL;
+  hmput(intmap, 15, 7);
+  hmput(intmap, 11, 3);
+  hmput(intmap,  9, 5);
+  STBDS_ASSERT(hmget(intmap, 9) == 5);
+  STBDS_ASSERT(hmget(intmap, 11) == 3);
+  STBDS_ASSERT(hmget(intmap, 15) == 7);
+  #endif
+  #endif
+
+  for (i=0; i < testsize; ++i)
+    stralloc(&sa, strkey(i));
+  strreset(&sa);
+
+  {
+    s.key = "a", s.value = 1;
+    shputs(strmap, s);
+    STBDS_ASSERT(*strmap[0].key == 'a');
+    STBDS_ASSERT(strmap[0].key == s.key);
+    STBDS_ASSERT(strmap[0].value == s.value);
+    shfree(strmap);
+  }
+
+  {
+    s.key = "a", s.value = 1;
+    sh_new_strdup(strmap);
+    shputs(strmap, s);
+    STBDS_ASSERT(*strmap[0].key == 'a');
+    STBDS_ASSERT(strmap[0].key != s.key);
+    STBDS_ASSERT(strmap[0].value == s.value);
+    shfree(strmap);
+  }
+
+  {
+    s.key = "a", s.value = 1;
+    sh_new_arena(strmap);
+    shputs(strmap, s);
+    STBDS_ASSERT(*strmap[0].key == 'a');
+    STBDS_ASSERT(strmap[0].key != s.key);
+    STBDS_ASSERT(strmap[0].value == s.value);
+    shfree(strmap);
+  }
+
+  for (j=0; j < 2; ++j) {
+    STBDS_ASSERT(shgeti(strmap,"foo") == -1);
+    if (j == 0)
+      sh_new_strdup(strmap);
+    else
+      sh_new_arena(strmap);
+    STBDS_ASSERT(shgeti(strmap,"foo") == -1);
+    shdefault(strmap, -2);
+    STBDS_ASSERT(shgeti(strmap,"foo") == -1);
+    for (i=0; i < testsize; i+=2)
+      shput(strmap, strkey(i), i*3);
+    for (i=0; i < testsize; i+=1)
+      if (i & 1) STBDS_ASSERT(shget(strmap, strkey(i)) == -2 );
+      else       STBDS_ASSERT(shget(strmap, strkey(i)) == i*3);
+    for (i=2; i < testsize; i+=4)
+      shdel(strmap, strkey(i)); // delete half the entries
+    for (i=0; i < testsize; i+=1)
+      if (i & 3) STBDS_ASSERT(shget(strmap, strkey(i)) == -2 );
+      else       STBDS_ASSERT(shget(strmap, strkey(i)) == i*3);
+    for (i=0; i < testsize; i+=1)
+      shdel(strmap, strkey(i)); // delete the rest of the entries
+    for (i=0; i < testsize; i+=1)
+      STBDS_ASSERT(shget(strmap, strkey(i)) == -2 );
+    shfree(strmap);
+  }
+
+  {
+    struct { char *key; char value; } *hash = NULL;
+    char name[4] = "jen";
+    shput(hash, "bob"   , 'h');
+    shput(hash, "sally" , 'e');
+    shput(hash, "fred"  , 'l');
+    shput(hash, "jen"   , 'x');
+    shput(hash, "doug"  , 'o');
+
+    shput(hash, name    , 'l');
+    shfree(hash);
+  }
+
+  for (i=0; i < testsize; i += 2) {
+    stbds_struct s = { i,i*2,i*3,i*4 };
+    hmput(map, s, i*5);
+  }
+
+  for (i=0; i < testsize; i += 1) {
+    stbds_struct s = { i,i*2,i*3  ,i*4 };
+    stbds_struct t = { i,i*2,i*3+1,i*4 };
+    if (i & 1) STBDS_ASSERT(hmget(map, s) == 0);
+    else       STBDS_ASSERT(hmget(map, s) == i*5);
+    if (i & 1) STBDS_ASSERT(hmget_ts(map, s, temp) == 0);
+    else       STBDS_ASSERT(hmget_ts(map, s, temp) == i*5);
+    //STBDS_ASSERT(hmget(map, t.key) == 0);
+  }
+
+  for (i=0; i < testsize; i += 2) {
+    stbds_struct s = { i,i*2,i*3,i*4 };
+    hmputs(map2, s);
+  }
+  hmfree(map);
+
+  for (i=0; i < testsize; i += 1) {
+    stbds_struct s = { i,i*2,i*3,i*4 };
+    stbds_struct t = { i,i*2,i*3+1,i*4 };
+    if (i & 1) STBDS_ASSERT(hmgets(map2, s.key).d == 0);
+    else       STBDS_ASSERT(hmgets(map2, s.key).d == i*4);
+    //STBDS_ASSERT(hmgetp(map2, t.key) == 0);
+  }
+  hmfree(map2);
+
+  for (i=0; i < testsize; i += 2) {
+    stbds_struct2 s = { { i,i*2 }, i*3,i*4, i*5 };
+    hmputs(map3, s);
+  }
+  for (i=0; i < testsize; i += 1) {
+    stbds_struct2 s = { { i,i*2}, i*3, i*4, i*5 };
+    stbds_struct2 t = { { i,i*2}, i*3+1, i*4, i*5 };
+    if (i & 1) STBDS_ASSERT(hmgets(map3, s.key).d == 0);
+    else       STBDS_ASSERT(hmgets(map3, s.key).d == i*5);
+    //STBDS_ASSERT(hmgetp(map3, t.key) == 0);
+  }
+#endif
+}
+#endif
+
+
+/*
+------------------------------------------------------------------------------
+This software is available under 2 licenses -- choose whichever you prefer.
+------------------------------------------------------------------------------
+ALTERNATIVE A - MIT License
+Copyright (c) 2019 Sean Barrett
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+------------------------------------------------------------------------------
+ALTERNATIVE B - Public Domain (www.unlicense.org)
+This is free and unencumbered software released into the public domain.
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
+software, either in source code form or as a compiled binary, for any purpose,
+commercial or non-commercial, and by any means.
+In jurisdictions that recognize copyright laws, the author or authors of this
+software dedicate any and all copyright interest in the software to the public
+domain. We make this dedication for the benefit of the public at large and to
+the detriment of our heirs and successors. We intend this dedication to be an
+overt act of relinquishment in perpetuity of all present and future rights to
+this software under copyright law.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+------------------------------------------------------------------------------
+*/
diff --git a/src/astnodes.c b/src/astnodes.c
deleted file mode 100644 (file)
index f2c6faf..0000000
+++ /dev/null
@@ -1,1406 +0,0 @@
-#include "astnodes.h"
-#include "parser.h"
-#include "utils.h"
-
-static const char* ast_node_names[] = {
-    "ERROR",
-    "PACKAGE",
-    "INCLUDE FILE",
-    "INCLUDE FOLDER",
-    "INCLUDE ALL IN FOLDER",
-    "INCLUDE LIBRARY PATH",
-    "MEMORY RESERVATION",
-
-    "BINDING",
-    "ALIAS",
-    "INJECTION",
-    "FUNCTION",
-    "OVERLOADED_FUNCTION",
-    "POLYMORPHIC PROC",
-    "POLYMORPH QUERY",
-    "INTERFACE",
-    "CONSTRAINT",
-    "CONSTRAINT SENTITEL",
-    "BLOCK",
-    "LOCAL",
-    "GLOBAL",
-    "SYMBOL",
-
-    "UN_OP",
-    "BIN_OP",
-
-    "COMPOUND",
-    "NAMED_VALUE",
-
-    "TYPE_START (BAD)",
-    "TYPE",
-    "BASIC_TYPE",
-    "POINTER_TYPE",
-    "FUNCTION_TYPE",
-    "ARRAY TYPE",
-    "SLICE TYPE",
-    "DYNARR TYPE",
-    "VARARG TYPE",
-    "STRUCT TYPE",
-    "POLYMORPHIC STRUCT TYPE",
-    "POLYMORPHIC STRUCT CALL TYPE",
-    "ENUM TYPE",
-    "TYPE_ALIAS",
-    "TYPE RAW ALIAS",
-    "COMPOUND TYPE",
-    "TYPE OF",
-    "DISTINCT TYPE",
-    "TYPE_END (BAD)",
-
-    "STRUCT MEMBER",
-    "ENUM VALUE",
-
-    "NUMERIC LITERAL",
-    "STRING LITERAL",
-    "PARAM",
-    "ARGUMENT",
-    "CALL",
-    "INTRINSIC CALL",
-    "RETURN",
-    "ADDRESS OF",
-    "DEREFERENCE",
-    "ARRAY ACCESS",
-    "SLICE",
-    "FIELD ACCESS",
-    "UNARY FIELD ACCESS",
-    "PIPE",
-    "METHOD_CALL",
-    "RANGE",
-    "SIZE OF",
-    "ALIGN OF",
-    "FILE CONTENTS",
-    "STRUCT LITERAL",
-    "ARRAY LITERAL",
-    "IF EXPRESSION",
-
-    "IF",
-    "FOR",
-    "WHILE",
-    "JUMP",
-    "USE",
-    "DEFER",
-    "SWITCH",
-    "CASE",
-
-    "SOLIDIFY",
-    "STATIC IF",
-    "STATIC ERROR",
-    "ADD OVERLOAD",
-    "OPERATOR OVERLOAD",
-    "EXPORT",
-    "DEFINED",
-    "TAG",
-    "INIT",
-    "LIBRARY",
-    "REMOVE",
-    "CALL SITE",
-
-    "CODE BLOCK",
-    "DIRECTIVE INSERT",
-    "MACRO",
-    "DO BLOCK",
-
-    "FOREIGN BLOCK",
-    "ZERO VALUE",
-
-    "NOTE",
-
-    "AST_NODE_KIND_COUNT",
-};
-
-const char* onyx_ast_node_kind_string(AstKind kind) {
-    return ast_node_names[kind];
-}
-
-const char *binaryop_string[Binary_Op_Count] = {
-    "+", "-", "*", "/", "%",
-    "==", "!=", "<", "<=", ">", ">=",
-    "&", "|", "^", "<<", ">>", ">>>",
-    "&&", "||",
-
-    "NONE",
-    "=", "+=", "-=", "*=", "/=", "%=",
-    "&=", "|=", "^=", "<<=", ">>=", ">>>=",
-    "NONE",
-
-    "|>", "..", "->",
-
-    "[]", "[]=", "^[]",
-};
-
-const char* entity_state_strings[Entity_State_Count] = {
-    "Error",
-    "Parse Builtin",
-    "Introduce Symbols",
-    "Parse",
-    "Resolve Symbols",
-    "Check Types",
-    "Code Gen",
-    "Finalized",
-    "Failed",
-};
-
-const char* entity_type_strings[Entity_Type_Count] = {
-    "Unknown",
-    "Error",
-    "Note",
-    "Add to Load Path",
-    "Load File",
-    "Binding (Declaration)",
-    "Use Package",
-    "Static If",
-    "String Literal",
-    "File Contents",
-    "Enum",
-    "Enum Value",
-    "Type Alias",
-    "Memory Reservation Type",
-    "Use",
-    "Interface",
-    "Constraint Check",
-    "Polymorphic Proc",
-    "Polymorph Query",
-    "Foreign Block",
-    "Macro",
-    "Foreign_Function Header",
-    "Temporary Function Header",
-    "Function Header",
-    "Global Header",
-    "Process Directive",
-    "Struct Member Default",
-    "Memory Reservation",
-    "Expression",
-    "Global",
-    "Overloaded_Function",
-    "Function",
-};
-
-AstNumLit* ast_reduce_type_compare(bh_allocator a, AstBinaryOp* node) {
-    AstType* left =  (AstType *) ast_reduce(a, node->left);
-    AstType* right = (AstType *) ast_reduce(a, node->right);
-
-    Type* left_type  = type_build_from_ast(context.ast_alloc, left);
-    Type* right_type = type_build_from_ast(context.ast_alloc, right);
-
-    AstNumLit* res = onyx_ast_node_new(a, sizeof(AstNumLit), Ast_Kind_NumLit);
-    res->token = node->token;
-    res->flags |= node->flags;
-    res->flags |= Ast_Flag_Comptime;
-    res->type_node = (AstType *) &basic_type_bool;
-    res->type = &basic_types[Basic_Kind_Bool];
-    res->next = node->next;
-
-    switch (node->operation) {
-        case Binary_Op_Equal:     res->value.l = left_type->id == right_type->id; break;
-        case Binary_Op_Not_Equal: res->value.l = left_type->id != right_type->id; break;
-        default: assert(("Bad case in ast_reduce_type_compare", 0));
-    }
-
-    return res;
-}
-
-#define REDUCE_BINOP_ALL(op) \
-    if (type_is_small_integer(res->type) || type_is_bool(res->type)) { \
-        res->value.i = left->value.i op right->value.i; \
-    } else if (type_is_integer(res->type) \
-        || res->type->Basic.kind == Basic_Kind_Int_Unsized \
-        || res->type->kind == Type_Kind_Enum) { \
-        res->value.l = left->value.l op right->value.l; \
-    } else if (res->type->Basic.kind == Basic_Kind_F32) { \
-        res->value.f = left->value.f op right->value.f; \
-    } else if (res->type->Basic.kind == Basic_Kind_F64 || res->type->Basic.kind == Basic_Kind_Float_Unsized) { \
-        res->value.d = left->value.d op right->value.d; \
-    } \
-    break;
-
-#define REDUCE_BINOP_INT(op) \
-    if (type_is_small_integer(res->type) || type_is_bool(res->type)) { \
-        res->value.i = left->value.i op right->value.i; \
-    } else if (type_is_integer(res->type) \
-        || res->type->Basic.kind == Basic_Kind_Int_Unsized \
-        || res->type->kind == Type_Kind_Enum) { \
-        res->value.l = left->value.l op right->value.l; \
-    } \
-    break;
-
-#define REDUCE_BINOP_BOOL(op) \
-    if (type_is_bool(res->type)) { \
-        res->value.i = left->value.i op right->value.i; \
-    } \
-    break;
-
-AstNumLit* ast_reduce_binop(bh_allocator a, AstBinaryOp* node) {
-    AstNumLit* left =  (AstNumLit *) ast_reduce(a, node->left);
-    AstNumLit* right = (AstNumLit *) ast_reduce(a, node->right);
-
-    if (node_is_type((AstNode *) left) && node_is_type((AstNode *) right)) {
-        if (node->operation == Binary_Op_Equal || node->operation == Binary_Op_Not_Equal) {
-            return (AstNumLit *) ast_reduce_type_compare(a, node);
-        }
-    }
-
-    if (left->kind != Ast_Kind_NumLit || right->kind != Ast_Kind_NumLit) {
-        node->left  = (AstTyped *) left;
-        node->right = (AstTyped *) right;
-        return (AstNumLit *) node;
-    }
-
-    AstNumLit* res = onyx_ast_node_new(a, sizeof(AstNumLit), Ast_Kind_NumLit);
-    res->token = node->token;
-    res->flags |= node->flags;
-    res->flags |= Ast_Flag_Comptime;
-    res->type_node = node->type_node;
-    res->type = node->type;
-    res->next = node->next;
-
-    switch (node->operation) {
-    case Binary_Op_Add:           REDUCE_BINOP_ALL(+);
-    case Binary_Op_Minus:         REDUCE_BINOP_ALL(-);
-    case Binary_Op_Multiply:      REDUCE_BINOP_ALL(*);
-    case Binary_Op_Divide:        REDUCE_BINOP_ALL(/);
-    case Binary_Op_Modulus:       REDUCE_BINOP_INT(%);
-
-    case Binary_Op_Equal:         REDUCE_BINOP_ALL(==);
-    case Binary_Op_Not_Equal:     REDUCE_BINOP_ALL(!=);
-    case Binary_Op_Less:          REDUCE_BINOP_ALL(<);
-    case Binary_Op_Less_Equal:    REDUCE_BINOP_ALL(<=);
-    case Binary_Op_Greater:       REDUCE_BINOP_ALL(>);
-    case Binary_Op_Greater_Equal: REDUCE_BINOP_ALL(>=);
-
-    case Binary_Op_And:           REDUCE_BINOP_INT(&);
-    case Binary_Op_Or:            REDUCE_BINOP_INT(|);
-    case Binary_Op_Xor:           REDUCE_BINOP_INT(^);
-    case Binary_Op_Shl:           REDUCE_BINOP_INT(<<);
-    case Binary_Op_Shr:           REDUCE_BINOP_INT(>>);
-    case Binary_Op_Sar:           REDUCE_BINOP_INT(>>);
-
-    case Binary_Op_Bool_And:      REDUCE_BINOP_BOOL(&&);
-    case Binary_Op_Bool_Or:       REDUCE_BINOP_BOOL(||);
-
-    default: break;
-    }
-
-    return res;
-}
-
-#define REDUCE_UNOP(op) \
-    if (type_is_small_integer(unop->type) || type_is_bool(unop->type)) { \
-        res->value.i = op (operand)->value.i; \
-    } else if (type_is_integer(unop->type) || unop->type->Basic.kind == Basic_Kind_Int_Unsized) { \
-        res->value.l = op (operand)->value.l; \
-    } else if (unop->type->Basic.kind == Basic_Kind_F32) { \
-        res->value.f = op (operand)->value.f; \
-    } else if (unop->type->Basic.kind == Basic_Kind_F64 || unop->type->Basic.kind == Basic_Kind_Float_Unsized) { \
-        res->value.d = op (operand)->value.d; \
-    } \
-    break;
-
-#define REDUCE_UNOP_INT(op) \
-    if (type_is_small_integer(unop->type) || type_is_bool(unop->type)) { \
-        res->value.i = op (operand)->value.i; \
-    } else if (type_is_integer(unop->type) || res->type->Basic.kind == Basic_Kind_Int_Unsized) { \
-        res->value.l = op (operand)->value.l; \
-    }
-
-AstTyped* ast_reduce_unaryop(bh_allocator a, AstUnaryOp* unop) {
-    // GROSS
-    AstNumLit* operand = (AstNumLit *) ast_reduce(a, unop->expr);
-    unop->expr = (AstTyped *) operand;
-
-    if (operand->kind != Ast_Kind_NumLit) {
-        return (AstTyped *) unop;
-    }
-
-    AstNumLit* res = onyx_ast_node_new(a, sizeof(AstNumLit), Ast_Kind_NumLit);
-    res->token = unop->token;
-    res->flags |= Ast_Flag_Comptime;
-    res->type_node = unop->type_node;
-    res->type = unop->type;
-    res->next = unop->next;
-
-    switch (unop->operation) {
-        case Unary_Op_Negate: REDUCE_UNOP(-);
-        case Unary_Op_Not: {
-            if (type_is_bool(res->type)) res->value.i = ! (operand)->value.i;
-            break;
-        }
-        case Unary_Op_Bitwise_Not: REDUCE_UNOP_INT(~);
-
-        case Unary_Op_Cast: {
-            #if 0
-            Type* from = operand->type;
-            Type* to   = unop->type;
-
-            if (from->kind == Type_Kind_Enum) from = from->Enum.backing;
-            if (to->kind == Type_Kind_Enum) to = to->Enum.backing;
-
-            if (from->kind != Type_Kind_Pointer && from->kind != Type_Kind_Basic) return (AstTyped *) unop;
-            if (to->kind != Type_Kind_Pointer && to->kind != Type_Kind_Basic) return (AstTyped *) unop;
-
-            b32 from_is_int=0, to_is_int=0;
-            i32 from_size,     to_size;
-
-            from_size   = type_size_of(from);
-            to_size     = type_size_of(to);
-
-            if (from->kind == Type_Kind_Basic) from_is_int = (from->flags & (Basic_Flag_Integer | Basic_Flag_Pointer)) != 0;
-            if (to->kind   == Type_Kind_Basic) to_is_int   = (to->flags   & (Basic_Flag_Integer | Basic_Flag_Pointer)) != 0;
-
-            if (from->kind == Type_Kind_Pointer) from_is_int = 1;
-            if (to->kind   == Type_Kind_Pointer) to_is_int = 1;
-
-            if (from_is_int == to_is_int && from_size == to_size) {
-                // If they are already equal, nothing to do
-                return (AstTyped *) res;
-            }
-
-            if (from_is_int && !to_is_int) {
-                if (from_size == to_size) {
-                    if (from_size == 4) {
-                        res->value.f = (f32) operand->value.i;
-                    }
-                    else if (from_size == 8) {
-                        res->value.d = (f64) operand->value.l;
-                    }
-                } else {
-                    if (from_size == 4) {
-                        res->value.d = (f64) operand->value.i;
-                    }
-                    else if (from_size == 8) {
-                        res->value.f = (f32) operand->value.l;
-                    }
-                }
-            } else {
-                if (from_size == to_size) {
-                    if (from_size == 4) {
-                        res->value.i = (i32) operand->value.f;
-                    }
-                    else if (from_size == 8) {
-                        res->value.l = (i64) operand->value.d;
-                    }
-                } else {
-                    if (from_size == 4) {
-                        res->value.l = (i64) operand->value.f;
-                    }
-                    else if (from_size == 8) {
-                        res->value.i = (i32) operand->value.d;
-                    }
-                }
-            }
-            break;
-
-            #endif
-        }
-
-        default: return (AstTyped *) unop;
-    }
-
-    return (AstTyped *) res;
-}
-
-AstTyped* ast_reduce(bh_allocator a, AstTyped* node) {
-    assert(node->flags & Ast_Flag_Comptime);
-
-    switch (node->kind) {
-        case Ast_Kind_Binary_Op:  return (AstTyped *) ast_reduce_binop(a, (AstBinaryOp *) node);
-        case Ast_Kind_Unary_Op:   return (AstTyped *) ast_reduce_unaryop(a, (AstUnaryOp *) node);
-        case Ast_Kind_Enum_Value: return (AstTyped *) ((AstEnumValue *) node)->value;
-        case Ast_Kind_Alias:      return (AstTyped *) ast_reduce(a, ((AstAlias *) node)->alias);
-        default:                  return node;
-    }
-}
-
-void promote_numlit_to_larger(AstNumLit* num) {
-    assert(num->type != NULL);
-
-    if (type_is_integer(num->type) && num->type->Basic.size <= 4) {
-        // NOTE: Int32, Int16, Int8
-        i64 val = (i64) num->value.i;
-        num->value.l = val;
-        num->type = &basic_types[Basic_Kind_I64];
-    } else if (num->type->Basic.size <= 4) {
-        // NOTE: Float32
-        f64 val = (f64) num->value.f;
-        num->value.d = val;
-        num->type = &basic_types[Basic_Kind_F64];
-    }
-}
-
-// NOTE: Returns 1 if the conversion was successful.
-b32 convert_numlit_to_type(AstNumLit* num, Type* to_type) {
-    if (num->type == NULL)
-        num->type = type_build_from_ast(context.ast_alloc, num->type_node);
-    assert(num->type);
-
-    if (types_are_compatible(num->type, to_type)) return 1;
-    if (!type_is_numeric(to_type)) return 0;
-
-    Type *type = to_type;
-    if (type->kind == Type_Kind_Enum) type = type->Enum.backing;
-    if (type->kind == Type_Kind_Distinct) type = type->Distinct.base_type;
-
-    if (num->type->Basic.kind == Basic_Kind_Int_Unsized) {
-
-        //
-        //  Integer literal auto cast rules:
-        //      - Up in size always works
-        //      - Down in size only works if value is in range of smaller type.
-        //      - Cast to float only works if value is less than the maximum precise value for float size.
-        //
-
-        if (type->Basic.flags & Basic_Flag_Integer) {
-            if (type->Basic.flags & Basic_Flag_Unsigned) {
-                u64 value = (u64) num->value.l;
-                if (type->Basic.size == 8) {
-                    num->type = to_type;
-                    return 1;
-                }
-                switch (type->Basic.size) {
-                    case 1: if (value <= 255) {
-                                num->type = to_type;
-                                return 1;
-                            }
-                    case 2: if (value <= 65535) {
-                                num->type = to_type;
-                                return 1;
-                            }
-                    case 4: if (value <= 4294967295) {
-                                num->type = to_type;
-                                return 1;
-                            }
-                }
-
-                onyx_report_error(num->token->pos, Error_Critical, "Unsigned integer constant with value '%l' does not fit into %d-bits.",
-                        num->value.l,
-                        type->Basic.size * 8);
-
-            } else {
-                i64 value = (i64) num->value.l;
-                switch (type->Basic.size) {
-                    case 1: if (-128ll <= value && value <= 127ll) {
-                                num->value.i = (i32) value;
-                                num->type = to_type;
-                                return 1;
-                            } break;
-                    case 2: if (-32768ll <= value && value <= 32767ll) {
-                                num->value.i = (i32) value;
-                                num->type = to_type;
-                                return 1;
-                            } break;
-                    case 4: if (-2147483648ll <= value && value <= 2147483647ll) {
-                                num->value.i = (i32) value;
-                                num->type = to_type;
-                                return 1;
-                            } break;
-                    case 8: {   num->type = to_type;
-                                return 1;
-                            } break;
-                }
-
-                onyx_report_error(num->token->pos, Error_Critical, "Integer constant with value '%l' does not fit into %d-bits.",
-                        num->value.l,
-                        type->Basic.size * 8);
-            }
-        }
-
-        else if (type->Basic.flags & Basic_Flag_Float) {
-            if (type->Basic.size == 4) {
-                // TODO(Brendan): Check these boundary conditions
-                if (bh_abs(num->value.l) >= (1 << 23)) {
-                    onyx_report_error(num->token->pos, Error_Critical, "Integer '%l' does not fit in 32-bit float exactly.", num->value.l);
-                    return 0;
-                }
-
-                num->type = to_type;
-                num->value.f = (f32) num->value.l;
-                return 1;
-            }
-            if (type->Basic.size == 8) {
-                // TODO(Brendan): Check these boundary conditions
-                if (bh_abs(num->value.l) >= (1ull << 52)) {
-                    onyx_report_error(num->token->pos, Error_Critical, "Integer '%l' does not fit in 64-bit float exactly.", num->value.l);
-                    return 0;
-                }
-
-                num->type = to_type;
-                num->value.d = (f64) num->value.l;
-                return 1;
-            }
-        }
-    }
-    else if (num->type->Basic.kind == Basic_Kind_Float_Unsized) {
-        // NOTE: Floats don't cast to integers implicitly.
-        if ((type->Basic.flags & Basic_Flag_Float) == 0) return 0;
-
-        if (type->Basic.kind == Basic_Kind_F32) {
-            num->value.f = (f32) num->value.d;
-        }
-
-        num->type = to_type;
-        return 1;
-    }
-    else if (num->type->Basic.kind == Basic_Kind_F32) {
-        // NOTE: Floats don't cast to integers implicitly.
-        if ((type->Basic.flags & Basic_Flag_Float) == 0) return 0;
-
-        if (type->Basic.kind == Basic_Kind_F64) {
-            num->value.d = (f64) num->value.f;
-            num->type = to_type;
-            return 1;
-        }
-    }
-
-    return 0;
-}
-
-// TODO: This function should be able return a "yield" condition. There
-// are a couple cases that need to yield in order to be correct, like
-// polymorphic functions with a typeof for the return type.
-TypeMatch unify_node_and_type_(AstTyped** pnode, Type* type, b32 permanent) {
-    AstTyped* node = *pnode;
-    if (type == NULL) return TYPE_MATCH_FAILED;
-    if (node == NULL) return TYPE_MATCH_FAILED;
-
-    if (node->kind == Ast_Kind_Struct_Literal && (node->type_node == NULL && node->type == NULL)) {
-        if (node->entity != NULL) return TYPE_MATCH_SUCCESS;
-        if (type->kind == Type_Kind_VarArgs) type = type->VarArgs.elem;
-
-        //
-        // If the structure literal has arguments, and the type is not constructable
-        // using a struct literal, then they cannot be unified. However, if no arguments
-        // are given, e.g. .{}, then any type should be matched, as that is the universal
-        // zero-value.
-        if (!type_is_sl_constructable(type)) {
-            AstStructLiteral *sl = (AstStructLiteral *) node;
-            if (bh_arr_length(sl->args.values) != 0 || bh_arr_length(sl->args.named_values) != 0) {
-                return TYPE_MATCH_FAILED;
-            }
-        }
-
-        // If this shouldn't make permanent changes and submit entities,
-        // just assume that it works and don't submit the entities.
-        if (!permanent) return TYPE_MATCH_SUCCESS;
-
-        node->type = type;
-
-        add_entities_for_node(NULL, (AstNode *) node, NULL, NULL);
-        return TYPE_MATCH_SUCCESS;
-    }
-
-    if (node->kind == Ast_Kind_Array_Literal && node->type == NULL) {
-        if (node->entity != NULL) return TYPE_MATCH_SUCCESS;
-
-        // If this shouldn't make permanent changes and submit entities,
-        // just assume that it works and don't submit the entities.
-        if (!permanent) return TYPE_MATCH_SUCCESS;
-
-        Type* array_type=NULL;
-        switch (type->kind) {
-            case Type_Kind_Array: array_type = type; break;
-            case Type_Kind_Slice: {
-                Type* elem_type = type->Slice.elem;
-                AstArrayLiteral* al = (AstArrayLiteral *) node;
-                array_type = type_make_array(context.ast_alloc, elem_type, bh_arr_length(al->values));
-
-                *pnode = (AstTyped *) make_cast(context.ast_alloc, node, type);
-
-                break;
-            }
-
-            default: assert(0);
-        }
-
-        node->type = array_type;
-        node->flags |= Ast_Flag_Array_Literal_Typed;
-
-        add_entities_for_node(NULL, (AstNode *) node, NULL, NULL);
-        return TYPE_MATCH_SUCCESS;
-    }
-
-    if (node->kind == Ast_Kind_Unary_Field_Access) {
-        AstType* ast_type = type->ast_type;
-        AstNode* resolved = try_symbol_resolve_from_node((AstNode *) ast_type, node->token);
-        if (resolved == NULL) {
-            if (context.cycle_detected) {
-                token_toggle_end(node->token);
-                char *closest = find_closest_symbol_in_node((AstNode *) ast_type, node->token->text);
-                token_toggle_end(node->token);
-
-                if (closest) {
-                    onyx_report_error(node->token->pos, Error_Critical, "'%b' does not exist in '%s'. Did you mean '%s'?",
-                        node->token->text, node->token->length,
-                        type_get_name(type),
-                        closest);
-                    return TYPE_MATCH_FAILED;
-                }
-            }
-
-            return TYPE_MATCH_YIELD;
-        }
-
-        if (permanent) *pnode = (AstTyped *) resolved;
-        return TYPE_MATCH_SUCCESS;
-    }
-
-    if (node->kind == Ast_Kind_Overloaded_Function) {
-        AstTyped* func = find_matching_overload_by_type(((AstOverloadedFunction *) node)->overloads, type);
-        if (func == NULL) return TYPE_MATCH_FAILED;
-        if (func == (AstTyped *) &node_that_signals_a_yield) return TYPE_MATCH_YIELD;
-
-        // HACK: It feels like there should be a better place to flag that a procedure was definitely used.
-        if (func->kind == Ast_Kind_Function)
-            func->flags |= Ast_Flag_Function_Used;
-
-        *pnode = func;
-        node = *pnode;
-    }
-
-    if (node->kind == Ast_Kind_Polymorphic_Proc) {
-        AstFunction* func = polymorphic_proc_lookup((AstFunction *) node, PPLM_By_Function_Type, type, node->token);
-        if (func == NULL) return TYPE_MATCH_FAILED;
-        if (func == (AstFunction *) &node_that_signals_a_yield) return TYPE_MATCH_YIELD;
-
-        *pnode = (AstTyped *) func;
-        node = *pnode;
-    }
-
-    // HACK: NullProcHack
-    // The null_proc matches any procedure, and because of that, will cause a runtime error if you
-    // try to call it.
-    if (type->kind == Type_Kind_Function && (node->flags & Ast_Flag_Proc_Is_Null) != 0) return TYPE_MATCH_SUCCESS;
-
-    // The normal case where everything works perfectly.
-    Type* node_type = get_expression_type(node);
-    if (types_are_compatible(node_type, type)) return TYPE_MATCH_SUCCESS;
-
-    Type* any_type = type_build_from_ast(context.ast_alloc, builtin_any_type);
-    if (any_type == NULL) return TYPE_MATCH_YIELD;
-    i64 any_id = any_type->id;
-
-    if (node_type && node_type->id != any_id && type->id == any_id) return TYPE_MATCH_SUCCESS;
-
-    // Here are some of the ways you can unify a node with a type if the type of the
-    // node does not match the given type:
-    //
-    // If the nodes type is a function type and that function has an automatic return
-    // value placeholder, fill in that placeholder with the actual type.
-    // :AutoReturnType
-    if (node_type && node_type->kind == Type_Kind_Function
-        && node_type->Function.return_type == &type_auto_return
-        && type->kind == Type_Kind_Function) {
-
-        node_type->Function.return_type = type->Function.return_type;
-        return TYPE_MATCH_SUCCESS;
-    }
-
-    // If the node is an auto cast (~~) node, then check to see if the cast is legal
-    // to the destination type, and if it is change the type to cast to.
-    if (node_is_auto_cast((AstNode *) node)) {
-        char* dummy;
-        Type* from_type = get_expression_type(((AstUnaryOp *) node)->expr);
-        if (!from_type || !cast_is_legal(from_type, type, &dummy)) {
-            return TYPE_MATCH_FAILED;
-
-        } else {
-            if (permanent) ((AstUnaryOp *) node)->type = type;
-            return TYPE_MATCH_SUCCESS;
-        }
-    }
-
-    // If the destination type is a slice, then automatically convert arrays, dynamic
-    // arrays, and var args, if they are the same type. This is big convenience feature
-    // that makes working with arrays much easier.
-    // [N] T  -> [] T
-    // [..] T -> [] T
-    // ..T    -> [] T
-    else if (node_type && type->kind == Type_Kind_Slice) {
-        if (node_type->kind == Type_Kind_Array || node_type->kind == Type_Kind_DynArray || node_type->kind == Type_Kind_VarArgs) {
-            char* dummy;
-            b32 legal = cast_is_legal(node_type, type, &dummy);
-            if (permanent && legal) {
-                *pnode = (AstTyped *) make_cast(context.ast_alloc, node, type);
-            }
-
-            return legal ? TYPE_MATCH_SUCCESS : TYPE_MATCH_FAILED;
-        }
-    }
-
-    // If the node is a numeric literal, try to convert it to the destination type.
-    else if (node->kind == Ast_Kind_NumLit) {
-        if (convert_numlit_to_type((AstNumLit *) node, type)) return TYPE_MATCH_SUCCESS;
-    }
-
-    // If the node is a compound expression, and it doesn't have a type created,
-    // recursive call this function with the individual components of the compound
-    // expression.
-    else if (node->kind == Ast_Kind_Compound) {
-        if (type->kind != Type_Kind_Compound) return TYPE_MATCH_FAILED;
-
-        AstCompound* compound = (AstCompound *) node;
-
-        u32 expr_count = bh_arr_length(compound->exprs);
-        if (expr_count != type->Compound.count) return TYPE_MATCH_FAILED;
-
-        fori (i, 0, (i64) expr_count) {
-            TypeMatch tm = unify_node_and_type_(&compound->exprs[i], type->Compound.types[i], permanent);
-            if (tm != TYPE_MATCH_SUCCESS) {
-                return tm;
-            }
-        }
-
-        compound->type = type_build_compound_type(context.ast_alloc, compound);
-
-        return TYPE_MATCH_SUCCESS;
-    }
-
-    else if (node->kind == Ast_Kind_If_Expression) {
-        AstIfExpression* if_expr = (AstIfExpression *) node;
-
-        TypeMatch true_success  = unify_node_and_type_(&if_expr->true_expr,  type, permanent);
-        TypeMatch false_success = unify_node_and_type_(&if_expr->false_expr, type, permanent);
-
-        if (true_success == TYPE_MATCH_SUCCESS && false_success == TYPE_MATCH_SUCCESS) {
-            if (permanent) if_expr->type = type;
-            return TYPE_MATCH_SUCCESS;
-
-        } else if (true_success == TYPE_MATCH_FAILED || false_success == TYPE_MATCH_FAILED) {
-            return TYPE_MATCH_FAILED;
-
-        } else {
-            return TYPE_MATCH_YIELD;
-        }
-    }
-
-    else if (node->kind == Ast_Kind_Alias) {
-        AstAlias* alias = (AstAlias *) node;
-        return unify_node_and_type_(&alias->alias, type, permanent);
-    }
-
-    else if (node->kind == Ast_Kind_Address_Of) {
-        AstAddressOf *address_of = (AstAddressOf *) node;
-        if (address_of->can_be_removed) {
-            if (!permanent) {
-                return unify_node_and_type_(&address_of->expr, type, permanent);
-
-            } else {
-                *pnode = (AstTyped *) address_of->expr;
-                return unify_node_and_type_(pnode, type, permanent);
-            }
-        }
-    }
-
-    else if (node->kind == Ast_Kind_Zero_Value) {
-        if (node_type == NULL) {
-            node->type = type;
-            return TYPE_MATCH_SUCCESS; // Shouldn't this be on the next line? And have node_type == node->type checked?
-        }
-    }
-
-    return TYPE_MATCH_FAILED;
-}
-
-Type* resolve_expression_type(AstTyped* node) {
-    if (node == NULL) return NULL;
-
-    if (node->kind == Ast_Kind_Compound) {
-        bh_arr_each(AstTyped *, expr, ((AstCompound *) node)->exprs) {
-            resolve_expression_type(*expr);
-        }
-
-        node->type = type_build_compound_type(context.ast_alloc, (AstCompound *) node);
-        return node->type;
-    }
-
-    if (node->kind == Ast_Kind_Argument) {
-        node->type = resolve_expression_type(((AstArgument *) node)->value);
-    }
-
-    if (node->kind == Ast_Kind_If_Expression) {
-        AstIfExpression* if_expr = (AstIfExpression *) node;
-
-        Type* ltype = resolve_expression_type(if_expr->true_expr);
-        unify_node_and_type(&if_expr->false_expr, ltype);
-
-        if_expr->type = ltype;
-    }
-
-    if (node->kind == Ast_Kind_Alias) {
-        AstAlias* alias = (AstAlias *) node;
-        alias->type = resolve_expression_type(alias->alias);
-    }
-
-    if (node_is_type((AstNode *) node)) {
-        return &basic_types[Basic_Kind_Type_Index];
-    }
-
-    if (node->kind == Ast_Kind_Array_Literal && node->type == NULL) {
-        AstArrayLiteral* al = (AstArrayLiteral *) node;
-        Type* elem_type = &basic_types[Basic_Kind_Void];
-        if (bh_arr_length(al->values) > 0) {
-            elem_type = resolve_expression_type(al->values[0]);
-        }
-
-        if (elem_type) {
-            node->type = type_make_array(context.ast_alloc, elem_type, bh_arr_length(al->values));
-            node->flags |= Ast_Flag_Array_Literal_Typed;
-
-            if (node->entity == NULL) {
-                add_entities_for_node(NULL, (AstNode *) node, NULL, NULL);
-            }
-        }
-    }
-
-    if (node->kind == Ast_Kind_Struct_Literal && node->type == NULL) {
-        AstStructLiteral* sl = (AstStructLiteral *) node;
-        if (sl->stnode || sl->type_node) return NULL;
-
-        // If values without names are given to a struct literal without
-        // a type, then we cannot implicitly build the type of the struct
-        // literal, as the name of every member cannot be known. Maybe we
-        // could implicitly do something like _1, _2, ... for the members
-        // that we not given names?
-        if (bh_arr_length(sl->args.values) > 0) {
-            return NULL;
-        }
-
-        sl->type = type_build_implicit_type_of_struct_literal(context.ast_alloc, sl);
-        if (sl->type) {
-            add_entities_for_node(NULL, (AstNode *) sl, NULL, NULL);
-        }
-    }
-
-    // If polymorphic procedures HAVE to have a type, most likely
-    // because they are part of a `typeof` expression, they are
-    // assigned a void type. This is cleared before the procedure
-    // is solidified.
-    if (node->kind == Ast_Kind_Polymorphic_Proc) {
-        node->type = &basic_types[Basic_Kind_Void];
-    }
-
-    if (node->kind == Ast_Kind_Macro) {
-        return resolve_expression_type((AstTyped *) ((AstMacro *) node)->body);
-    }
-
-    if (node->type == NULL)
-        node->type = type_build_from_ast(context.ast_alloc, node->type_node);
-
-    if (node->kind == Ast_Kind_NumLit && node->type->kind == Type_Kind_Basic) {
-        if (node->type->Basic.kind == Basic_Kind_Int_Unsized) {
-            b32 big    = bh_abs(((AstNumLit *) node)->value.l) >= (1ull << 32);
-            b32 unsign = ((AstNumLit *) node)->was_hex_literal;
-
-            if      ( big && !unsign) convert_numlit_to_type((AstNumLit *) node, &basic_types[Basic_Kind_I64]);
-            else if ( big &&  unsign) convert_numlit_to_type((AstNumLit *) node, &basic_types[Basic_Kind_U64]);
-            else if (!big && !unsign) convert_numlit_to_type((AstNumLit *) node, &basic_types[Basic_Kind_I32]);
-            else if (!big &&  unsign) convert_numlit_to_type((AstNumLit *) node, &basic_types[Basic_Kind_U32]);
-        }
-        else if (node->type->Basic.kind == Basic_Kind_Float_Unsized) {
-            convert_numlit_to_type((AstNumLit *) node, &basic_types[Basic_Kind_F64]);
-        }
-    }
-
-    return node->type;
-}
-
-i64 get_expression_integer_value(AstTyped* node, b32 *is_valid) {
-    if (!node) return 0;
-
-    resolve_expression_type(node);
-
-    if (is_valid) *is_valid = 1;
-
-    if (node->kind == Ast_Kind_NumLit && type_is_integer(node->type)) {
-        return ((AstNumLit *) node)->value.l;
-    }
-
-    if (node->kind == Ast_Kind_NumLit && type_is_bool(node->type)) {
-        return ((AstNumLit *) node)->value.i;
-    }
-
-    if (node->kind == Ast_Kind_Argument) {
-        return get_expression_integer_value(((AstArgument *) node)->value, is_valid);
-    }
-
-    if (node->kind == Ast_Kind_Size_Of) {
-        return ((AstSizeOf *) node)->size;
-    }
-
-    if (node->kind == Ast_Kind_Align_Of) {
-        return ((AstAlignOf *) node)->alignment;
-    }
-
-    if (node->kind == Ast_Kind_Alias) {
-        return get_expression_integer_value(((AstAlias *) node)->alias, is_valid);
-    }
-
-    if (node->kind == Ast_Kind_Enum_Value) {
-        return get_expression_integer_value(((AstEnumValue *) node)->value, is_valid);
-    }
-
-    if (node_is_type((AstNode*) node)) {
-        Type* type = type_build_from_ast(context.ast_alloc, (AstType *) node);
-        if (type) return type->id;
-    }
-
-    if (is_valid) *is_valid = 0;
-    return 0;
-}
-
-char *get_expression_string_value(AstTyped* node, b32 *out_is_valid) {
-    resolve_expression_type(node);
-
-    if (out_is_valid) *out_is_valid = 1;
-
-    if (node->kind == Ast_Kind_StrLit) {
-        AstStrLit *str = (AstStrLit *) node;
-
-        // CLEANUP: Maybe this should allocate on the heap?
-        // I guess if in all cases the memory is allocated on the heap,
-        // then the caller can free the memory.
-        i8* strdata = bh_alloc_array(global_heap_allocator, i8, str->token->length + 1);
-        i32 length  = string_process_escape_seqs(strdata, str->token->text, str->token->length);
-        strdata[length] = '\0';
-
-        return strdata;
-    }
-
-    if (node->kind == Ast_Kind_Alias) {
-        return get_expression_string_value(((AstAlias *) node)->alias, out_is_valid);
-    }
-
-    if (out_is_valid) *out_is_valid = 0;
-    return NULL;
-}
-
-static const b32 cast_legality[][12] = {
-    /* I8  */ { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
-    /* U8  */ { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
-    /* I16 */ { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
-    /* U16 */ { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
-    /* I32 */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
-    /* U32 */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
-    /* I64 */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 },
-    /* U64 */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 },
-    /* F32 */ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0 },
-    /* F64 */ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0 },
-    /* PTR */ { 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0 },
-    /* TYP */ { 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1,}
-};
-
-b32 cast_is_legal(Type* from_, Type* to_, char** err_msg) {
-    Type* from = from_;
-    Type* to   = to_;
-
-    if (from == NULL) {
-        if (err_msg) *err_msg = "'from' is null. (Compiler Error)";
-        return 0;
-    }
-    if (to == NULL) {
-        if (err_msg) *err_msg = "'to' is null. (Compiler Error)";
-        return 0;
-    }
-
-    if (from_->id == to_->id) return 1;
-
-    if (from->kind == Type_Kind_Enum) from = from->Enum.backing;
-    if (to->kind == Type_Kind_Enum) to = to->Enum.backing;
-
-    if (from->kind == Type_Kind_Struct || to->kind == Type_Kind_Struct) {
-        *err_msg = "Cannot cast to or from a struct.";
-        return 0;
-    }
-
-    // CLEANUP: These error messages should be a lot better and actually
-    // provide the types of the things in question.
-    if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_Array) {
-        if (!types_are_compatible(to->Slice.elem, from->Array.elem)) {
-            *err_msg = "Array to slice cast is not valid here because the types are different.";
-            return 0;
-        } else {
-            return 1;
-        }
-    }
-
-    if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_DynArray) {
-        //if (!types_are_compatible(to->Slice.elem, from->DynArray.elem)) {
-        if (type_size_of(to->Slice.elem) != type_size_of(from->DynArray.elem)) {
-            *err_msg = "Dynmaic array to slice cast is not valid here because the types are different sizes.";
-            return 0;
-        } else {
-            return 1;
-        }
-    }
-
-    if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_VarArgs) {
-        if (!types_are_compatible(to->Slice.elem, from->VarArgs.elem)) {
-            *err_msg = "Variadic argument to slice cast is not valid here because the types are different.";
-            return 0;
-        } else {
-            return 1;
-        }
-    }
-
-    if (to->kind == Type_Kind_Distinct) {
-        if (!types_are_compatible(to->Distinct.base_type, from)) {
-            // :BadErrorMessage
-            *err_msg = "Cannot convert to a distinct type using the wrong base type.";
-            return 0;
-        } else {
-            return 1;
-        }
-    }
-
-    if (from->kind == Type_Kind_Distinct) {
-        if (!types_are_compatible(from->Distinct.base_type, to)) {
-            // :BadErrorMessage
-            *err_msg = "Cannot convert from a distinct type to the wrong destination type.";
-            return 0;
-        } else {
-            return 1;
-        }
-    }
-
-    if (from->kind == Type_Kind_Slice || to->kind == Type_Kind_Slice) {
-        if ((from->kind != Type_Kind_Slice || to->kind != Type_Kind_Slice)
-            || to->Slice.elem->kind != Type_Kind_Pointer || from->Slice.elem->kind != Type_Kind_Pointer
-            || !types_are_compatible(from->Slice.elem, to->Slice.elem)) {
-            *err_msg = "Cannot only cast between slice types when both are a slice of compatible pointers.";
-            return 0;
-        } else {
-            return 1;
-        }
-    }
-
-    if (from->kind == Type_Kind_DynArray || to->kind == Type_Kind_DynArray) {
-        *err_msg = "Cannot cast to or from a dynamic array.";
-        return 0;
-    }
-
-    if (to->kind == Type_Kind_Function) {
-        *err_msg = "Cannot cast to a function.";
-        return 0;
-    }
-
-    if (   (type_is_simd(to) && !type_is_simd(from))
-        || (!type_is_simd(to) && type_is_simd(from))) {
-        *err_msg = "Can only perform a SIMD cast between SIMD types.";
-        return 0;
-    }
-
-    if (from->kind == Type_Kind_Basic && from->Basic.kind == Basic_Kind_Void) {
-        *err_msg = "Cannot cast from void.";
-        return 0;
-    }
-    i32 fromidx = -1, toidx = -1;
-    if (from->Basic.flags & Basic_Flag_Pointer || from->kind == Type_Kind_Array) {
-        fromidx = 10;
-    }
-    else if (from->Basic.flags & Basic_Flag_Integer) {
-        b32 unsign = (from->Basic.flags & Basic_Flag_Unsigned) != 0;
-
-        fromidx = log2_dumb(from->Basic.size) * 2 + unsign;
-    }
-    else if (from->Basic.flags & Basic_Flag_Float) {
-        if      (from->Basic.size == 4) fromidx = 8;
-        else if (from->Basic.size == 8) fromidx = 9;
-    }
-    else if (from->Basic.flags & Basic_Flag_Boolean) {
-        fromidx = 0;
-    }
-    else if (from->Basic.flags & Basic_Flag_Type_Index) {
-        fromidx = 11;
-    }
-
-    if (to->Basic.flags & Basic_Flag_Pointer || to->kind == Type_Kind_Array) {
-        toidx = 10;
-    }
-    else if (to->Basic.flags & Basic_Flag_Integer) {
-        b32 unsign = (to->Basic.flags & Basic_Flag_Unsigned) != 0;
-
-        toidx = log2_dumb(to->Basic.size) * 2 + unsign;
-    }
-    else if (to->Basic.flags & Basic_Flag_Float) {
-        if      (to->Basic.size == 4) toidx = 8;
-        else if (to->Basic.size == 8) toidx = 9;
-    }
-    else if (to->Basic.flags & Basic_Flag_Boolean) {
-        toidx = 0;
-    }
-    else if (to->Basic.flags & Basic_Flag_Type_Index) {
-        toidx = 11;
-    }
-
-    if (fromidx != -1 && toidx != -1) {
-        if (!cast_legality[fromidx][toidx]) {
-            *err_msg = bh_aprintf(global_heap_allocator, "Cast from '%s' to '%s' is not allowed.", type_get_name(from_), type_get_name(to_));
-            return 0;
-        }
-    } else {
-        *err_msg = bh_aprintf(global_heap_allocator, "Cast from '%s' to '%s' is not allowed.", type_get_name(from_), type_get_name(to_));
-        return 0;
-    }
-
-    *err_msg = NULL;
-    return 1;
-}
-
-char* get_function_name(AstFunction* func) {
-    if (func->kind != Ast_Kind_Function) return "<procedure>";
-
-    if (func->name != NULL) return func->name;
-
-    if (func->exported_name != NULL) {
-        return bh_aprintf(global_scratch_allocator,
-                "EXPORTED:%b",
-                func->exported_name->text,
-                func->exported_name->length);
-    }
-
-    return "<anonymous procedure>";
-}
-
-AstNode* strip_aliases(AstNode* n) {
-    if (n == NULL) return n;
-
-    while (n->kind == Ast_Kind_Alias) n = (AstNode *) ((AstAlias *) n)->alias;
-
-    return n;
-}
-
-AstNumLit* make_bool_literal(bh_allocator a, b32 b) {
-    AstNumLit* bl = onyx_ast_node_new(a, sizeof(AstNumLit), Ast_Kind_NumLit);
-    bl->flags |= Ast_Flag_Comptime;
-    bl->type_node = (AstType *) &basic_type_bool;
-
-    bl->value.i = b ? 1 : 0;
-    return bl;
-}
-
-AstNumLit* make_int_literal(bh_allocator a, i64 i) {
-    AstNumLit* num = onyx_ast_node_new(a, sizeof(AstNumLit), Ast_Kind_NumLit);
-    num->flags |= Ast_Flag_Comptime;
-
-    if (bh_abs(i) >= ((u64) 1 << 32))
-        num->type_node = (AstType *) &basic_type_i64;
-    else
-        num->type_node = (AstType *) &basic_type_i32;
-
-    num->value.l = i;
-    return num;
-}
-
-AstNumLit* make_float_literal(bh_allocator a, f64 d) {
-    // NOTE: Use convert_numlit_to_type to make this a concrete float
-    AstNumLit* num = onyx_ast_node_new(a, sizeof(AstNumLit), Ast_Kind_NumLit);
-    num->flags |= Ast_Flag_Comptime;
-    num->type_node = (AstType *) &basic_type_float_unsized;
-    num->value.d = d;
-    return num;
-}
-
-AstRangeLiteral* make_range_literal(bh_allocator a, AstTyped* low, AstTyped* high) {
-    AstRangeLiteral* rl = onyx_ast_node_new(a, sizeof(AstRangeLiteral), Ast_Kind_Range_Literal);
-    rl->type = builtin_range_type_type;
-    rl->low = low;
-    rl->high = high;
-    return rl;
-}
-
-AstBinaryOp* make_binary_op(bh_allocator a, BinaryOp operation, AstTyped* left, AstTyped* right) {
-    AstBinaryOp* binop_node = onyx_ast_node_new(a, sizeof(AstBinaryOp), Ast_Kind_Binary_Op);
-    binop_node->left  = left;
-    binop_node->right = right;
-    binop_node->operation = operation;
-    return binop_node;
-}
-
-AstArgument* make_argument(bh_allocator a, AstTyped* value) {
-    AstArgument* arg = onyx_ast_node_new(a, sizeof(AstArgument), Ast_Kind_Argument);
-    if (value->token) arg->token = value->token;
-    arg->value = value;
-    arg->type = value->type;
-    arg->next = NULL;
-    arg->va_kind = VA_Kind_Not_VA;
-    return arg;
-}
-
-AstFieldAccess* make_field_access(bh_allocator a, AstTyped* node, char* field) {
-    AstFieldAccess* fa = onyx_ast_node_new(a, sizeof(AstFieldAccess), Ast_Kind_Field_Access);
-    if (node->token) fa->token = node->token;
-    fa->field = field;
-    fa->expr = node;
-
-    return fa;
-}
-
-AstAddressOf* make_address_of(bh_allocator a, AstTyped* node) {
-    AstAddressOf* ao = onyx_ast_node_new(a, sizeof(AstAddressOf), Ast_Kind_Address_Of);
-    if (node->token) ao->token = node->token;
-    ao->expr = node;
-
-    return ao;
-}
-
-AstLocal* make_local(bh_allocator a, OnyxToken* token, AstType* type_node) {
-    AstLocal* local = onyx_ast_node_new(a, sizeof(AstLocal), Ast_Kind_Local);
-    local->token = token;
-    local->type_node = type_node;
-
-    return local;
-}
-
-AstNode* make_symbol(bh_allocator a, OnyxToken* sym) {
-    AstNode* symbol = onyx_ast_node_new(a, sizeof(AstNode), Ast_Kind_Symbol);
-    symbol->token = sym;
-    return symbol;
-}
-
-AstUnaryOp* make_cast(bh_allocator a, AstTyped* expr, Type* to) {
-    AstUnaryOp* cast = onyx_ast_node_new(a, sizeof(AstUnaryOp), Ast_Kind_Unary_Op);
-    cast->token = expr->token;
-    cast->operation = Unary_Op_Cast;
-    cast->expr = expr;
-    cast->type = to;
-    return cast;
-}
-
-AstZeroValue* make_zero_value(bh_allocator a, OnyxToken* token, Type* type) {
-    AstZeroValue* zero_value = onyx_ast_node_new(a, sizeof(AstZeroValue), Ast_Kind_Zero_Value);
-    zero_value->token = token;
-    zero_value->flags |= Ast_Flag_Comptime;
-    zero_value->type = type;
-    return zero_value;
-}
-
-void arguments_initialize(Arguments* args) {
-    if (args->values == NULL)       bh_arr_new(global_heap_allocator, args->values, 2);
-    if (args->named_values == NULL) bh_arr_new(global_heap_allocator, args->named_values, 2);
-
-    // CLEANUP: I'm not sure if I need to initialize these to NULL values, but it doesn't hurt.
-    fori (i, 0, 2) {
-        args->values[i] = NULL;
-        args->named_values[i] = NULL;
-    }
-
-    args->used_argument_count = -1;
-}
-
-void arguments_ensure_length(Arguments* args, u32 count) {
-    // Make the array big enough
-    bh_arr_grow(args->values, count);
-
-    // NULL initialize the new elements
-    fori (i, bh_arr_length(args->values), count) args->values[i] = NULL;
-
-    // Set the actual length to the count, but never let it decrease in size
-    bh_arr_set_length(args->values, bh_max(count, (u32) bh_arr_length(args->values)));
-}
-
-void arguments_copy(Arguments* dest, Arguments* src) {
-    dest->used_argument_count = -1;
-    dest->named_values = src->named_values;
-
-    bh_arr_grow(dest->values, (u32) bh_arr_length(src->values));
-    bh_arr_set_length(dest->values, (u32) bh_arr_length(src->values));
-    bh_arr_each(AstTyped*, arg, dest->values) *arg = NULL;
-    fori (i, 0, bh_arr_length(src->values)) dest->values[i] = src->values[i];
-}
-
-// In clone, the named_values are not copied. This is used in find_matching_overload_by_arguments since it doesn't need them to be copied.
-void arguments_clone(Arguments* dest, Arguments* src) {
-    dest->used_argument_count = -1;
-    dest->named_values = src->named_values;
-    dest->values = bh_arr_copy(global_heap_allocator, src->values);
-}
-
-void arguments_deep_clone(bh_allocator a, Arguments* dest, Arguments* src) {
-    dest->used_argument_count = -1;
-    dest->values = NULL;
-    dest->named_values = NULL;
-
-    bh_arr_new(global_heap_allocator, dest->named_values, bh_arr_length(src->named_values));
-    bh_arr_new(global_heap_allocator, dest->values, bh_arr_length(src->values));
-
-    bh_arr_each(AstNamedValue *, nv, src->named_values)
-        bh_arr_push(dest->named_values, (AstNamedValue *) ast_clone(a, *nv));
-
-    bh_arr_each(AstTyped *, val, src->values)
-        bh_arr_push(dest->values, (AstTyped *) ast_clone(a, (AstNode *) *val));
-}
-
-void arguments_remove_baked(Arguments* args) {
-    fori (i, 0, bh_arr_length(args->values)) {
-        if (args->values[i]->kind != Ast_Kind_Argument) continue;
-        if (!((AstArgument *) args->values[i])->is_baked) continue;
-
-        bh_arr_deleten(args->values, i, 1);
-        i--;
-    }
-
-    fori (i, 0, bh_arr_length(args->named_values)) {
-        if (args->named_values[i]->value->kind != Ast_Kind_Argument) continue;
-        if (!((AstArgument *) args->named_values[i]->value)->is_baked) continue;
-
-        bh_arr_deleten(args->named_values, i, 1);
-        i--;
-    }
-}
-
-void arguments_clear_baked_flags(Arguments* args) {
-    bh_arr_each(AstTyped *, arg, args->values) {
-        if ((*arg)->kind == Ast_Kind_Argument) {
-            ((AstArgument *) *arg)->is_baked = 0;
-        }
-    }
-
-    bh_arr_each(AstNamedValue *, arg, args->named_values) {
-        if ((*arg)->value->kind == Ast_Kind_Argument) {
-            ((AstArgument *) (*arg)->value)->is_baked = 0;
-        }
-    }
-}
-
-// GROSS: Using void* to avoid having to cast everything.
-const char* node_get_type_name(void* node) {
-    if (node_is_type((AstNode *) node)) return "type_expr";
-
-    if (((AstNode *) node)->kind == Ast_Kind_Argument) {
-        return node_get_type_name(((AstArgument *) node)->value);
-    }
-
-    if (((AstNode *) node)->kind == Ast_Kind_Polymorphic_Proc) {
-        return "polymorphic procedure";
-    }
-
-    return type_get_name(((AstTyped *) node)->type);
-}
-
-b32 static_if_resolution(AstIf* static_if) {
-    if (static_if->kind != Ast_Kind_Static_If) return 0;
-
-    // assert(condition_value->kind == Ast_Kind_NumLit); // This should be right, right?
-    i64 value = get_expression_integer_value(static_if->cond, NULL);
-
-    return value != 0;
-}
-
-AstPolyCallType* convert_call_to_polycall(AstCall* call) {
-    // HACK HACK HACK
-    AstPolyCallType *pct = onyx_ast_node_new(context.ast_alloc, sizeof(AstPolyCallType), Ast_Kind_Poly_Call_Type);
-    pct->token = call->token;
-    pct->__unused = call->next;
-    pct->callee = (AstType *) call->callee;
-    pct->params = (AstNode **) call->args.values;
-    bh_arr_each(AstNode *, pp, pct->params) {
-        if ((*pp)->kind == Ast_Kind_Argument) {
-            *pp = (AstNode *) (*(AstArgument **) pp)->value;
-        }
-    }
-
-    return pct;
-}
diff --git a/src/builtins.c b/src/builtins.c
deleted file mode 100644 (file)
index b6eacef..0000000
+++ /dev/null
@@ -1,555 +0,0 @@
-#include "astnodes.h"
-#include "types.h"
-#include "errors.h"
-#include "utils.h"
-
-AstBasicType basic_type_void      = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_Void]  };
-AstBasicType basic_type_bool      = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_Bool]  };
-AstBasicType basic_type_i8        = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_I8]    };
-AstBasicType basic_type_u8        = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_U8]    };
-AstBasicType basic_type_i16       = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_I16]   };
-AstBasicType basic_type_u16       = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_U16]   };
-AstBasicType basic_type_i32       = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_I32]   };
-AstBasicType basic_type_u32       = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_U32]   };
-AstBasicType basic_type_i64       = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_I64]   };
-AstBasicType basic_type_u64       = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_U64]   };
-AstBasicType basic_type_f32       = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_F32]   };
-AstBasicType basic_type_f64       = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_F64]   };
-AstBasicType basic_type_rawptr    = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_Rawptr] };
-AstBasicType basic_type_type_expr = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_Type_Index] };
-
-// NOTE: Types used for numeric literals
-AstBasicType basic_type_int_unsized   = { Ast_Kind_Basic_Type, 0, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_Int_Unsized] };
-AstBasicType basic_type_float_unsized = { Ast_Kind_Basic_Type, 0, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_Float_Unsized] };
-
-static OnyxToken simd_token = { Token_Type_Symbol, 0, "", { 0 } };
-AstBasicType basic_type_i8x16 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, &simd_token, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_I8X16] };
-AstBasicType basic_type_i16x8 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, &simd_token, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_I16X8] };
-AstBasicType basic_type_i32x4 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, &simd_token, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_I32X4] };
-AstBasicType basic_type_i64x2 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, &simd_token, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_I64X2] };
-AstBasicType basic_type_f32x4 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, &simd_token, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_F32X4] };
-AstBasicType basic_type_f64x2 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, &simd_token, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_F64X2] };
-AstBasicType basic_type_v128  = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, &simd_token, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_V128]  };
-
-// HACK
-// :AutoReturnType
-Type         type_auto_return = { 0 };
-AstBasicType basic_type_auto_return = { Ast_Kind_Basic_Type, 0, &simd_token, NULL, NULL, 0, NULL, &type_auto_return };
-
-OnyxToken builtin_package_token = { Token_Type_Symbol, 7, "builtin ", { 0 } };
-
-static OnyxToken builtin_heap_start_token = { Token_Type_Symbol, 12, "__heap_start ", { 0 } };
-static OnyxToken builtin_stack_top_token  = { Token_Type_Symbol, 11, "__stack_top ",  { 0 } };
-static OnyxToken builtin_tls_base_token   = { Token_Type_Symbol, 10, "__tls_base ",  { 0 } };
-static OnyxToken builtin_tls_size_token   = { Token_Type_Symbol, 10, "__tls_size ",  { 0 } };
-AstGlobal builtin_heap_start  = { Ast_Kind_Global, Ast_Flag_Const, &builtin_heap_start_token, NULL, NULL, (AstType *) &basic_type_rawptr, NULL };
-AstGlobal builtin_stack_top   = { Ast_Kind_Global, 0, &builtin_stack_top_token, NULL, NULL, (AstType *) &basic_type_rawptr, NULL };
-AstGlobal builtin_tls_base    = { Ast_Kind_Global, 0, &builtin_tls_base_token, NULL, NULL, (AstType *) &basic_type_rawptr, NULL };
-AstGlobal builtin_tls_size    = { Ast_Kind_Global, 0, &builtin_tls_size_token, NULL, NULL, (AstType *) &basic_type_u32, NULL };
-
-AstType  *builtin_string_type;
-AstType  *builtin_cstring_type;
-AstType  *builtin_range_type;
-Type     *builtin_range_type_type;
-AstType  *builtin_vararg_type;
-Type     *builtin_vararg_type_type;
-AstTyped *builtin_context_variable;
-AstType  *builtin_allocator_type;
-AstType  *builtin_iterator_type;
-AstType  *builtin_callsite_type;
-AstType  *builtin_any_type;
-AstType  *builtin_code_type;
-AstType  *builtin_link_options_type;
-
-AstTyped    *type_table_node = NULL;
-AstTyped    *foreign_blocks_node = NULL;
-AstType     *foreign_block_type = NULL;
-AstTyped    *tagged_procedures_node = NULL;
-AstFunction *builtin_initialize_data_segments = NULL;
-AstFunction *builtin_run_init_procedures = NULL;
-bh_arr(AstFunction *) init_procedures = NULL;
-
-const BuiltinSymbol builtin_symbols[] = {
-    { NULL, "void",       (AstNode *) &basic_type_void },
-    { NULL, "bool",       (AstNode *) &basic_type_bool },
-    { NULL, "i8",         (AstNode *) &basic_type_i8 },
-    { NULL, "u8",         (AstNode *) &basic_type_u8 },
-    { NULL, "i16",        (AstNode *) &basic_type_i16 },
-    { NULL, "u16",        (AstNode *) &basic_type_u16 },
-    { NULL, "i32",        (AstNode *) &basic_type_i32 },
-    { NULL, "u32",        (AstNode *) &basic_type_u32 },
-    { NULL, "i64",        (AstNode *) &basic_type_i64 },
-    { NULL, "u64",        (AstNode *) &basic_type_u64 },
-    { NULL, "f32",        (AstNode *) &basic_type_f32 },
-    { NULL, "f64",        (AstNode *) &basic_type_f64 },
-    { NULL, "rawptr",     (AstNode *) &basic_type_rawptr },
-    { NULL, "type_expr",  (AstNode *) &basic_type_type_expr },
-
-    { "simd", "i8x16",    (AstNode *) &basic_type_i8x16 },
-    { "simd", "i16x8",    (AstNode *) &basic_type_i16x8 },
-    { "simd", "i32x4",    (AstNode *) &basic_type_i32x4 },
-    { "simd", "i64x2",    (AstNode *) &basic_type_i64x2 },
-    { "simd", "f32x4",    (AstNode *) &basic_type_f32x4 },
-    { "simd", "f64x2",    (AstNode *) &basic_type_f64x2 },
-    { "simd", "v128",     (AstNode *) &basic_type_v128  },
-
-    { "builtin", "__heap_start", (AstNode *) &builtin_heap_start },
-    { "builtin", "__stack_top",  (AstNode *) &builtin_stack_top },
-    { "builtin", "__tls_base",   (AstNode *) &builtin_tls_base },
-    { "builtin", "__tls_size",   (AstNode *) &builtin_tls_size },
-
-    { NULL, NULL, NULL },
-};
-
-IntrinsicTable intrinsic_table;
-
-static IntrinsicMap builtin_intrinsics[] = {
-    { "memory_size",  ONYX_INTRINSIC_MEMORY_SIZE },
-    { "memory_grow",  ONYX_INTRINSIC_MEMORY_GROW },
-    { "memory_copy",  ONYX_INTRINSIC_MEMORY_COPY },
-    { "memory_fill",  ONYX_INTRINSIC_MEMORY_FILL },
-
-    { "__initialize", ONYX_INTRINSIC_INITIALIZE },
-
-    { "clz_i32",      ONYX_INTRINSIC_I32_CLZ },
-    { "ctz_i32",      ONYX_INTRINSIC_I32_CTZ },
-    { "popcnt_i32",   ONYX_INTRINSIC_I32_POPCNT },
-    { "and_i32",      ONYX_INTRINSIC_I32_AND },
-    { "or_i32",       ONYX_INTRINSIC_I32_OR },
-    { "xor_i32",      ONYX_INTRINSIC_I32_XOR },
-    { "shl_i32",      ONYX_INTRINSIC_I32_SHL },
-    { "slr_i32",      ONYX_INTRINSIC_I32_SLR },
-    { "sar_i32",      ONYX_INTRINSIC_I32_SAR },
-    { "rotl_i32",     ONYX_INTRINSIC_I32_ROTL },
-    { "rotr_i32",     ONYX_INTRINSIC_I32_ROTR },
-
-    { "clz_i64",      ONYX_INTRINSIC_I64_CLZ },
-    { "ctz_i64",      ONYX_INTRINSIC_I64_CTZ },
-    { "popcnt_i64",   ONYX_INTRINSIC_I64_POPCNT },
-    { "and_i64",      ONYX_INTRINSIC_I64_AND },
-    { "or_i64",       ONYX_INTRINSIC_I64_OR },
-    { "xor_i64",      ONYX_INTRINSIC_I64_XOR },
-    { "shl_i64",      ONYX_INTRINSIC_I64_SHL },
-    { "slr_i64",      ONYX_INTRINSIC_I64_SLR },
-    { "sar_i64",      ONYX_INTRINSIC_I64_SAR },
-    { "rotl_i64",     ONYX_INTRINSIC_I64_ROTL },
-    { "rotr_i64",     ONYX_INTRINSIC_I64_ROTR },
-
-    { "abs_f32",      ONYX_INTRINSIC_F32_ABS },
-    { "ceil_f32",     ONYX_INTRINSIC_F32_CEIL },
-    { "floor_f32",    ONYX_INTRINSIC_F32_FLOOR },
-    { "trunc_f32",    ONYX_INTRINSIC_F32_TRUNC },
-    { "nearest_f32",  ONYX_INTRINSIC_F32_NEAREST },
-    { "sqrt_f32",     ONYX_INTRINSIC_F32_SQRT },
-    { "min_f32",      ONYX_INTRINSIC_F32_MIN },
-    { "max_f32",      ONYX_INTRINSIC_F32_MAX },
-    { "copysign_f32", ONYX_INTRINSIC_F32_COPYSIGN },
-
-    { "abs_f64",      ONYX_INTRINSIC_F64_ABS },
-    { "ceil_f64",     ONYX_INTRINSIC_F64_CEIL },
-    { "floor_f64",    ONYX_INTRINSIC_F64_FLOOR },
-    { "trunc_f64",    ONYX_INTRINSIC_F64_TRUNC },
-    { "nearest_f64",  ONYX_INTRINSIC_F64_NEAREST },
-    { "sqrt_f64",     ONYX_INTRINSIC_F64_SQRT },
-    { "min_f64",      ONYX_INTRINSIC_F64_MIN },
-    { "max_f64",      ONYX_INTRINSIC_F64_MAX },
-    { "copysign_f64", ONYX_INTRINSIC_F64_COPYSIGN },
-
-
-    // SIMD Intrinsics
-    { "v128_const", ONYX_INTRINSIC_V128_CONST },
-    { "i8x16_const", ONYX_INTRINSIC_I8X16_CONST },
-    { "i16x8_const", ONYX_INTRINSIC_I16X8_CONST },
-    { "i32x4_const", ONYX_INTRINSIC_I32X4_CONST },
-    { "i64x2_const", ONYX_INTRINSIC_I64X2_CONST },
-    { "f32x4_const", ONYX_INTRINSIC_F32X4_CONST },
-    { "f64x2_const", ONYX_INTRINSIC_F64X2_CONST },
-    { "i8x16_shuffle", ONYX_INTRINSIC_I8X16_SHUFFLE },
-
-    { "i8x16_extract_lane_s", ONYX_INTRINSIC_I8X16_EXTRACT_LANE_S },
-    { "i8x16_extract_lane_u", ONYX_INTRINSIC_I8X16_EXTRACT_LANE_U },
-       { "i8x16_replace_lane",   ONYX_INTRINSIC_I8X16_REPLACE_LANE },
-    { "i16x8_extract_lane_s", ONYX_INTRINSIC_I16X8_EXTRACT_LANE_S },
-       { "i16x8_extract_lane_u", ONYX_INTRINSIC_I16X8_EXTRACT_LANE_U },
-       { "i16x8_replace_lane",   ONYX_INTRINSIC_I16X8_REPLACE_LANE },
-       { "i32x4_extract_lane",   ONYX_INTRINSIC_I32X4_EXTRACT_LANE },
-       { "i32x4_replace_lane",   ONYX_INTRINSIC_I32X4_REPLACE_LANE },
-    { "i64x2_extract_lane",   ONYX_INTRINSIC_I64X2_EXTRACT_LANE },
-       { "i64x2_replace_lane",   ONYX_INTRINSIC_I64X2_REPLACE_LANE },
-       { "f32x4_extract_lane",   ONYX_INTRINSIC_F32X4_EXTRACT_LANE },
-       { "f32x4_replace_lane",   ONYX_INTRINSIC_F32X4_REPLACE_LANE },
-       { "f64x2_extract_lane",   ONYX_INTRINSIC_F64X2_EXTRACT_LANE },
-       { "f64x2_replace_lane",   ONYX_INTRINSIC_F64X2_REPLACE_LANE },
-
-    { "i8x16_swizzle", ONYX_INTRINSIC_I8X16_SWIZZLE },
-    { "i8x16_splat",   ONYX_INTRINSIC_I8X16_SPLAT },
-       { "i16x8_splat",   ONYX_INTRINSIC_I16X8_SPLAT },
-    { "i32x4_splat",   ONYX_INTRINSIC_I32X4_SPLAT },
-       { "i64x2_splat",   ONYX_INTRINSIC_I64X2_SPLAT },
-    { "f32x4_splat",   ONYX_INTRINSIC_F32X4_SPLAT },
-       { "f64x2_splat",   ONYX_INTRINSIC_F64X2_SPLAT },
-
-    { "i8x16_eq",   ONYX_INTRINSIC_I8X16_EQ },
-       { "i8x16_neq",  ONYX_INTRINSIC_I8X16_NEQ },
-    { "i8x16_lt_s", ONYX_INTRINSIC_I8X16_LT_S },
-       { "i8x16_lt_u", ONYX_INTRINSIC_I8X16_LT_U },
-    { "i8x16_gt_s", ONYX_INTRINSIC_I8X16_GT_S },
-       { "i8x16_gt_u", ONYX_INTRINSIC_I8X16_GT_U },
-    { "i8x16_le_s", ONYX_INTRINSIC_I8X16_LE_S },
-       { "i8x16_le_u", ONYX_INTRINSIC_I8X16_LE_U },
-    { "i8x16_ge_s", ONYX_INTRINSIC_I8X16_GE_S },
-       { "i8x16_ge_u", ONYX_INTRINSIC_I8X16_GE_U },
-
-    { "i16x8_eq",   ONYX_INTRINSIC_I16X8_EQ },
-       { "i16x8_neq",  ONYX_INTRINSIC_I16X8_NEQ },
-    { "i16x8_lt_s", ONYX_INTRINSIC_I16X8_LT_S },
-       { "i16x8_lt_u", ONYX_INTRINSIC_I16X8_LT_U },
-    { "i16x8_gt_s", ONYX_INTRINSIC_I16X8_GT_S },
-       { "i16x8_gt_u", ONYX_INTRINSIC_I16X8_GT_U },
-    { "i16x8_le_s", ONYX_INTRINSIC_I16X8_LE_S },
-       { "i16x8_le_u", ONYX_INTRINSIC_I16X8_LE_U },
-    { "i16x8_ge_s", ONYX_INTRINSIC_I16X8_GE_S },
-       { "i16x8_ge_u", ONYX_INTRINSIC_I16X8_GE_U },
-
-    { "i32x4_eq",   ONYX_INTRINSIC_I32X4_EQ },
-       { "i32x4_neq",  ONYX_INTRINSIC_I32X4_NEQ },
-    { "i32x4_lt_s", ONYX_INTRINSIC_I32X4_LT_S },
-       { "i32x4_lt_u", ONYX_INTRINSIC_I32X4_LT_U },
-    { "i32x4_gt_s", ONYX_INTRINSIC_I32X4_GT_S },
-       { "i32x4_gt_u", ONYX_INTRINSIC_I32X4_GT_U },
-    { "i32x4_le_s", ONYX_INTRINSIC_I32X4_LE_S },
-       { "i32x4_le_u", ONYX_INTRINSIC_I32X4_LE_U },
-    { "i32x4_ge_s", ONYX_INTRINSIC_I32X4_GE_S },
-       { "i32x4_ge_u", ONYX_INTRINSIC_I32X4_GE_U },
-
-    { "f32x4_eq",  ONYX_INTRINSIC_F32X4_EQ },
-       { "f32x4_neq", ONYX_INTRINSIC_F32X4_NEQ },
-    { "f32x4_lt",  ONYX_INTRINSIC_F32X4_LT },
-       { "f32x4_gt",  ONYX_INTRINSIC_F32X4_GT },
-    { "f32x4_le",  ONYX_INTRINSIC_F32X4_LE },
-       { "f32x4_ge",  ONYX_INTRINSIC_F32X4_GE },
-
-    { "f64x2_eq",  ONYX_INTRINSIC_F64X2_EQ },
-       { "f64x2_neq", ONYX_INTRINSIC_F64X2_NEQ },
-    { "f64x2_lt",  ONYX_INTRINSIC_F64X2_LT },
-       { "f64x2_gt",  ONYX_INTRINSIC_F64X2_GT },
-    { "f64x2_le",  ONYX_INTRINSIC_F64X2_LE },
-       { "f64x2_ge",  ONYX_INTRINSIC_F64X2_GE },
-
-    { "v128_not",       ONYX_INTRINSIC_V128_NOT },
-       { "v128_and",       ONYX_INTRINSIC_V128_AND },
-       { "v128_andnot",    ONYX_INTRINSIC_V128_ANDNOT },
-    { "v128_or",        ONYX_INTRINSIC_V128_OR },
-       { "v128_xor",       ONYX_INTRINSIC_V128_XOR },
-       { "v128_bitselect", ONYX_INTRINSIC_V128_BITSELECT },
-
-    { "i8x16_abs",            ONYX_INTRINSIC_I8X16_ABS },
-       { "i8x16_neg",            ONYX_INTRINSIC_I8X16_NEG },
-    { "i8x16_any_true",       ONYX_INTRINSIC_I8X16_ANY_TRUE },
-       { "i8x16_all_true",       ONYX_INTRINSIC_I8X16_ALL_TRUE },
-    { "i8x16_bitmask",        ONYX_INTRINSIC_I8X16_BITMASK },
-    { "i8x16_narrow_i16x8_s", ONYX_INTRINSIC_I8X16_NARROW_I16X8_S },
-       { "i8x16_narrow_i16x8_u", ONYX_INTRINSIC_I8X16_NARROW_I16X8_U },
-    { "i8x16_shl",            ONYX_INTRINSIC_I8X16_SHL },
-       { "i8x16_shr_s",          ONYX_INTRINSIC_I8X16_SHR_S },
-       { "i8x16_shr_u",          ONYX_INTRINSIC_I8X16_SHR_U },
-    { "i8x16_add",            ONYX_INTRINSIC_I8X16_ADD },
-       { "i8x16_add_sat_s",      ONYX_INTRINSIC_I8X16_ADD_SAT_S },
-       { "i8x16_add_sat_u",      ONYX_INTRINSIC_I8X16_ADD_SAT_U },
-    { "i8x16_sub",            ONYX_INTRINSIC_I8X16_SUB },
-       { "i8x16_sub_sat_s",      ONYX_INTRINSIC_I8X16_SUB_SAT_S },
-       { "i8x16_sub_sat_u",      ONYX_INTRINSIC_I8X16_SUB_SAT_U },
-    { "i8x16_min_s",          ONYX_INTRINSIC_I8X16_MIN_S },
-       { "i8x16_min_u",          ONYX_INTRINSIC_I8X16_MIN_U },
-    { "i8x16_max_s",          ONYX_INTRINSIC_I8X16_MAX_S },
-       { "i8x16_max_u",          ONYX_INTRINSIC_I8X16_MAX_U },
-    { "i8x16_avgr_u",         ONYX_INTRINSIC_I8X16_AVGR_U },
-
-    { "i16x8_abs",                ONYX_INTRINSIC_I16X8_ABS },
-       { "i16x8_neg",                ONYX_INTRINSIC_I16X8_NEG },
-    { "i16x8_any_true",           ONYX_INTRINSIC_I16X8_ANY_TRUE },
-       { "i16x8_all_true",           ONYX_INTRINSIC_I16X8_ALL_TRUE },
-    { "i16x8_bitmask",            ONYX_INTRINSIC_I16X8_BITMASK },
-    { "i16x8_narrow_i32x4_s",     ONYX_INTRINSIC_I16X8_NARROW_I32X4_S },
-       { "i16x8_narrow_i32x4_u",     ONYX_INTRINSIC_I16X8_NARROW_I32X4_U },
-    { "i16x8_widen_low_i8x16_s",  ONYX_INTRINSIC_I16X8_WIDEN_LOW_I8X16_S },
-       { "i16x8_widen_high_i8x16_s", ONYX_INTRINSIC_I16X8_WIDEN_HIGH_I8X16_S },
-    { "i16x8_widen_low_i8x16_u",  ONYX_INTRINSIC_I16X8_WIDEN_LOW_I8X16_U },
-       { "i16x8_widen_high_i8x16_u", ONYX_INTRINSIC_I16X8_WIDEN_HIGH_I8X16_U },
-    { "i16x8_shl",                ONYX_INTRINSIC_I16X8_SHL },
-       { "i16x8_shr_s",              ONYX_INTRINSIC_I16X8_SHR_S },
-       { "i16x8_shr_u",              ONYX_INTRINSIC_I16X8_SHR_U },
-    { "i16x8_add",                ONYX_INTRINSIC_I16X8_ADD },
-       { "i16x8_add_sat_s",          ONYX_INTRINSIC_I16X8_ADD_SAT_S },
-       { "i16x8_add_sat_u",          ONYX_INTRINSIC_I16X8_ADD_SAT_U },
-    { "i16x8_sub",                ONYX_INTRINSIC_I16X8_SUB },
-       { "i16x8_sub_sat_s",          ONYX_INTRINSIC_I16X8_SUB_SAT_S },
-       { "i16x8_sub_sat_u",          ONYX_INTRINSIC_I16X8_SUB_SAT_U },
-    { "i16x8_mul",                ONYX_INTRINSIC_I16X8_MUL },
-    { "i16x8_min_s",              ONYX_INTRINSIC_I16X8_MIN_S },
-       { "i16x8_min_u",              ONYX_INTRINSIC_I16X8_MIN_U },
-    { "i16x8_max_s",              ONYX_INTRINSIC_I16X8_MAX_S },
-       { "i16x8_max_u",              ONYX_INTRINSIC_I16X8_MAX_U },
-    { "i16x8_avgr_u",             ONYX_INTRINSIC_I16X8_AVGR_U },
-
-    { "i32x4_abs",                ONYX_INTRINSIC_I32X4_ABS },
-       { "i32x4_neg",                ONYX_INTRINSIC_I32X4_NEG },
-    { "i32x4_any_true",           ONYX_INTRINSIC_I32X4_ANY_TRUE },
-       { "i32x4_all_true",           ONYX_INTRINSIC_I32X4_ALL_TRUE },
-    { "i32x4_bitmask",            ONYX_INTRINSIC_I32X4_BITMASK },
-    { "i32x4_widen_low_i16x8_s",  ONYX_INTRINSIC_I32X4_WIDEN_LOW_I16X8_S },
-       { "i32x4_widen_high_i16x8_s", ONYX_INTRINSIC_I32X4_WIDEN_HIGH_I16X8_S },
-    { "i32x4_widen_low_i16x8_u",  ONYX_INTRINSIC_I32X4_WIDEN_LOW_I16X8_U },
-       { "i32x4_widen_high_i16x8_u", ONYX_INTRINSIC_I32X4_WIDEN_HIGH_I16X8_U },
-    { "i32x4_shl",                ONYX_INTRINSIC_I32X4_SHL },
-       { "i32x4_shr_s",              ONYX_INTRINSIC_I32X4_SHR_S },
-       { "i32x4_shl_u",              ONYX_INTRINSIC_I32X4_SHR_U },
-    { "i32x4_add",                ONYX_INTRINSIC_I32X4_ADD },
-       { "i32x4_sub",                ONYX_INTRINSIC_I32X4_SUB },
-       { "i32x4_mul",                ONYX_INTRINSIC_I32X4_MUL },
-    { "i32x4_min_s",              ONYX_INTRINSIC_I32X4_MIN_S },
-       { "i32x4_min_u",              ONYX_INTRINSIC_I32X4_MIN_U },
-    { "i32x4_max_s",              ONYX_INTRINSIC_I32X4_MAX_S },
-       { "i32x4_max_u",              ONYX_INTRINSIC_I32X4_MAX_U },
-
-    { "i64x2_neg",   ONYX_INTRINSIC_I64X2_NEG },
-       { "i64x2_shl",   ONYX_INTRINSIC_I64X2_SHL },
-    { "i64x2_shr_s", ONYX_INTRINSIC_I64X2_SHR_S },
-       { "i64x2_shr_u", ONYX_INTRINSIC_I64X2_SHR_U },
-    { "i64x2_add",   ONYX_INTRINSIC_I64X2_ADD },
-       { "i64x2_sub",   ONYX_INTRINSIC_I64X2_SUB },
-       { "i64x2_mul",   ONYX_INTRINSIC_I64X2_MUL },
-
-    { "f32x4_abs",  ONYX_INTRINSIC_F32X4_ABS },
-       { "f32x4_neg",  ONYX_INTRINSIC_F32X4_NEG },
-       { "f32x4_sqrt", ONYX_INTRINSIC_F32X4_SQRT },
-    { "f32x4_add",  ONYX_INTRINSIC_F32X4_ADD },
-       { "f32x4_sub",  ONYX_INTRINSIC_F32X4_SUB },
-    { "f32x4_mul",  ONYX_INTRINSIC_F32X4_MUL },
-       { "f32x4_div",  ONYX_INTRINSIC_F32X4_DIV },
-    { "f32x4_min",  ONYX_INTRINSIC_F32X4_MIN },
-       { "f32x4_max",  ONYX_INTRINSIC_F32X4_MAX },
-
-    { "f64x2_abs",  ONYX_INTRINSIC_F64X2_ABS },
-       { "f64x2_neg",  ONYX_INTRINSIC_F64X2_NEG },
-       { "f64x2_sqrt", ONYX_INTRINSIC_F64X2_SQRT },
-    { "f64x2_add",  ONYX_INTRINSIC_F64X2_ADD },
-       { "f64x2_sub",  ONYX_INTRINSIC_F64X2_SUB },
-    { "f64x2_mul",  ONYX_INTRINSIC_F64X2_MUL },
-       { "f64x2_div",  ONYX_INTRINSIC_F64X2_DIV },
-    { "f64x2_min",  ONYX_INTRINSIC_F64X2_MIN },
-       { "f64x2_max",  ONYX_INTRINSIC_F64X2_MAX },
-
-    { "i32x4_trunc_sat_f32x4_s", ONYX_INTRINSIC_I32X4_TRUNC_SAT_F32X4_S },
-    { "i32x4_trunc_sat_f32x4_u", ONYX_INTRINSIC_I32X4_TRUNC_SAT_F32X4_U },
-    { "f32x4_convert_i32x4_s",   ONYX_INTRINSIC_F32X4_CONVERT_I32X4_S },
-    { "f32x4_convert_i32x4_u",   ONYX_INTRINSIC_F32X4_CONVERT_I32X4_U },
-
-    { "__atomic_wait",    ONYX_INTRINSIC_ATOMIC_WAIT    },
-    { "__atomic_notify",  ONYX_INTRINSIC_ATOMIC_NOTIFY  },
-
-    { "__atomic_fence",   ONYX_INTRINSIC_ATOMIC_FENCE   },
-
-    { "__atomic_load",    ONYX_INTRINSIC_ATOMIC_LOAD    },
-    { "__atomic_store",   ONYX_INTRINSIC_ATOMIC_STORE   },
-    { "__atomic_add",     ONYX_INTRINSIC_ATOMIC_ADD     },
-    { "__atomic_sub",     ONYX_INTRINSIC_ATOMIC_SUB     },
-    { "__atomic_and",     ONYX_INTRINSIC_ATOMIC_AND     },
-    { "__atomic_or",      ONYX_INTRINSIC_ATOMIC_OR      },
-    { "__atomic_xor",     ONYX_INTRINSIC_ATOMIC_XOR     },
-    { "__atomic_xchg",    ONYX_INTRINSIC_ATOMIC_XCHG    },
-    { "__atomic_cmpxchg", ONYX_INTRINSIC_ATOMIC_CMPXCHG },
-
-    { NULL, ONYX_INTRINSIC_UNDEFINED },
-};
-
-bh_arr(OverloadOption) operator_overloads[Binary_Op_Count] = { 0 };
-
-void initialize_builtins(bh_allocator a) {
-    // HACK
-    builtin_package_token.text = bh_strdup(global_heap_allocator, builtin_package_token.text);
-
-    BuiltinSymbol* bsym = (BuiltinSymbol *) &builtin_symbols[0];
-    while (bsym->sym != NULL) {
-        if (bsym->package == NULL)
-            symbol_builtin_introduce(context.global_scope, bsym->sym, bsym->node);
-        else {
-            Package* p = package_lookup_or_create(bsym->package, context.global_scope, a, context.global_scope->created_at);
-            assert(p);
-
-            symbol_builtin_introduce(p->scope, bsym->sym, bsym->node);
-        }
-        bsym++;
-    }
-
-    Package* p = package_lookup_or_create("builtin", context.global_scope, a, context.global_scope->created_at);
-
-    builtin_string_type = (AstType *) symbol_raw_resolve(p->scope, "str");
-    if (builtin_string_type == NULL) {
-        onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'str' struct not found in builtin package.");
-        return;
-    }
-
-    builtin_cstring_type = (AstType *) symbol_raw_resolve(p->scope, "cstr");
-    if (builtin_cstring_type == NULL) {
-        onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'cstr' type not found in builtin package.");
-        return;
-    }
-
-    builtin_range_type = (AstType *) symbol_raw_resolve(p->scope, "range");
-    if (builtin_range_type == NULL) {
-        onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'range' struct not found in builtin package.");
-        return;
-    }
-
-    builtin_vararg_type = (AstType *) symbol_raw_resolve(p->scope, "vararg");
-    if (builtin_range_type == NULL) {
-        onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'vararg' struct not found in builtin package.");
-        return;
-    }
-
-    builtin_context_variable = (AstTyped *) symbol_raw_resolve(p->scope, "context");
-    if (builtin_context_variable == NULL) {
-        onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'context' variable not found in builtin package.");
-        return;
-    }
-
-    builtin_allocator_type = (AstType *) symbol_raw_resolve(p->scope, "Allocator");
-    if (builtin_allocator_type == NULL) {
-        onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'Allocator' struct not found in builtin package.");
-        return;
-    }
-
-    builtin_iterator_type = (AstType *) symbol_raw_resolve(p->scope, "Iterator");
-    if (builtin_iterator_type == NULL) {
-        onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'Iterator' struct not found in builtin package.");
-        return;
-    }
-
-    builtin_callsite_type = (AstType *) symbol_raw_resolve(p->scope, "CallSite");
-    if (builtin_callsite_type == NULL) {
-        onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'CallSite' struct not found in builtin package.");
-        return;
-    }
-
-    builtin_any_type = (AstType *) symbol_raw_resolve(p->scope, "any");
-    if (builtin_any_type == NULL) {
-        onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'any' struct not found in builtin package.");
-        return;
-    }
-
-    builtin_code_type = (AstType *) symbol_raw_resolve(p->scope, "Code");
-    if (builtin_code_type == NULL) {
-        onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'Code' struct not found in builtin package.");
-        return;
-    }
-
-    builtin_initialize_data_segments = (AstFunction *) symbol_raw_resolve(p->scope, "__initialize_data_segments");
-    if (builtin_initialize_data_segments == NULL || builtin_initialize_data_segments->kind != Ast_Kind_Function) {
-        onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'__initialize_data_segments' procedure not found in builtin package.");
-        return;
-    }
-
-    builtin_run_init_procedures = (AstFunction *) symbol_raw_resolve(p->scope, "__run_init_procedures");
-    if (builtin_run_init_procedures == NULL || builtin_run_init_procedures->kind != Ast_Kind_Function) {
-        onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'__run_init_procedures' procedure not found.");
-        return;
-    }
-
-    builtin_link_options_type = (AstType *) symbol_raw_resolve(p->scope, "Link_Options");
-    if (builtin_link_options_type == NULL) {
-        onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'Link_Options' type not found.");
-        return;
-    }
-
-    bh_arr_new(global_heap_allocator, init_procedures, 4);
-
-    fori (i, 0, Binary_Op_Count) {
-        bh_arr_new(global_heap_allocator, operator_overloads[i], 4); 
-    }
-
-    IntrinsicMap* intrinsic = &builtin_intrinsics[0];
-    while (intrinsic->name != NULL) {
-        shput(intrinsic_table, intrinsic->name, intrinsic->intrinsic);
-        intrinsic++;
-    }
-}
-
-void initalize_special_globals() {
-    Package *p = package_lookup("runtime.info");
-    if (p != NULL) {
-        type_table_node     = (AstTyped *) symbol_raw_resolve(p->scope, "type_table");
-        foreign_blocks_node = (AstTyped *) symbol_raw_resolve(p->scope, "foreign_blocks");
-        foreign_block_type  = (AstType *)  symbol_raw_resolve(p->scope, "foreign_block");
-        tagged_procedures_node = (AstTyped *) symbol_raw_resolve(p->scope, "tagged_procedures");
-    }
-}
-
-void introduce_build_options(bh_allocator a) {
-    Package* p = package_lookup_or_create("runtime", context.global_scope, a, context.global_scope->created_at);
-
-    AstType* Runtime_Type = (AstType *) symbol_raw_resolve(p->scope, "Runtime");
-    if (Runtime_Type == NULL) {
-        onyx_report_error((OnyxFilePos) {0}, Error_Critical, "'Runtime' type not found in package runtime.");
-        return;
-    }
-
-    AstNumLit* runtime_type = make_int_literal(a, context.options->runtime);
-    runtime_type->type_node = Runtime_Type;
-    add_entities_for_node(NULL, (AstNode *) runtime_type, NULL, NULL);
-    symbol_builtin_introduce(p->scope, "runtime", (AstNode *) runtime_type);
-
-    AstNumLit* multi_threaded = make_int_literal(a, context.options->use_multi_threading);
-    multi_threaded->type_node = (AstType *) &basic_type_bool;
-    symbol_builtin_introduce(p->scope, "Multi_Threading_Enabled", (AstNode *) multi_threaded);
-
-    AstNumLit* wait_notify_available = make_int_literal(a, context.options->use_multi_threading && context.options->runtime == Runtime_Js);
-    wait_notify_available->type_node = (AstType *) &basic_type_bool;
-    symbol_builtin_introduce(p->scope, "Wait_Notify_Available", (AstNode *) wait_notify_available);
-
-    i32 os;
-    #ifdef _BH_LINUX
-        os = 1;
-    #endif
-    #ifdef _BH_WINDOWS
-        os = 2;
-    #endif
-
-    AstType* OS_Type = (AstType *) symbol_raw_resolve(p->scope, "OS");
-    if (OS_Type == NULL) {
-        onyx_report_error((OnyxFilePos) {0}, Error_Critical, "'OS' type not found in package runtime.");
-        return;
-    }
-
-    AstNumLit* os_type = make_int_literal(a, os);
-    os_type->type_node = OS_Type;
-    add_entities_for_node(NULL, (AstNode *) os_type, NULL, NULL);
-    symbol_builtin_introduce(p->scope, "compiler_os", (AstNode *) os_type);
-
-    i32 arch = 0;
-    #if defined(__x86_64__) || defined(_M_X64)
-        arch = 1; // X86_64;
-    #elif defined(i386) || defined(__i386__) || defined(__i386) || defined(_M_IX86)
-        arch = 2; // X86_32;
-    #elif defined(__aarch64__) || defined(_M_ARM64)
-        arch = 3; // AARCH64;
-    #endif
-
-    AstType* Arch_Type = (AstType *) symbol_raw_resolve(p->scope, "Arch");
-    if (Arch_Type == NULL) {
-        onyx_report_error((OnyxFilePos) {0}, Error_Critical, "'Arch' type not found in package runtime.");
-        return;
-    }
-
-    AstNumLit* arch_type = make_int_literal(a, arch);
-    arch_type->type_node = Arch_Type;
-    add_entities_for_node(NULL, (AstNode *) arch_type, NULL, NULL);
-    symbol_builtin_introduce(p->scope, "arch", (AstNode *) arch_type);
-
-    if (context.options->generate_foreign_info) {
-        AstNumLit* foreign_info = make_int_literal(a, 1);
-        foreign_info->type_node = (AstType *) &basic_type_bool;
-        symbol_builtin_introduce(p->scope, "Generated_Foreign_Info", (AstNode *) foreign_info);
-    }
-}
-
diff --git a/src/checker.c b/src/checker.c
deleted file mode 100644 (file)
index 333e331..0000000
+++ /dev/null
@@ -1,3023 +0,0 @@
-#define BH_DEBUG
-#include "parser.h"
-#include "utils.h"
-
-// All of the `check` functions return a boolean that signals if an issue
-// was reached while processing the node. These error booleans propagate
-// up the call stack until they reach `check_entity`.
-
-#define CHECK(kind, ...) do { \
-    CheckStatus cs = check_ ## kind (__VA_ARGS__); \
-    if (cs > Check_Errors_Start) return cs; \
-    } while (0)
-
-#define YIELD(loc, msg) do { \
-    if (context.cycle_detected) { \
-        onyx_report_error(loc, Error_Waiting_On, msg); \
-        return Check_Error; \
-    } else { \
-        return Check_Yield_Macro; \
-    } \
-    } while (0)
-
-#define YIELD_(loc, msg, ...) do { \
-    if (context.cycle_detected) { \
-        onyx_report_error(loc, Error_Waiting_On, msg, __VA_ARGS__); \
-        return Check_Error; \
-    } else { \
-        return Check_Yield_Macro; \
-    } \
-    } while (0)
-
-#define YIELD_ERROR(loc, msg) do { \
-    if (context.cycle_detected) { \
-        onyx_report_error(loc, Error_Critical, msg); \
-        return Check_Error; \
-    } else { \
-        return Check_Yield_Macro; \
-    } \
-    } while (0)
-
-#define ERROR(loc, msg) do { \
-    onyx_report_error(loc, Error_Critical, msg); \
-    return Check_Error; \
-    } while (0)
-
-#define ERROR_(loc, msg, ...) do { \
-    onyx_report_error(loc, Error_Critical, msg, __VA_ARGS__); \
-    return Check_Error; \
-    } while (0)
-
-#define TYPE_CHECK_(expr, type, type_name)                                                      \
-    TypeMatch type_name;                                                                        \
-    type_name = unify_node_and_type(expr, type);                                                \
-    if (type_name == TYPE_MATCH_YIELD) YIELD((*expr)->token->pos, "Waiting on type checking."); \
-    if (type_name == TYPE_MATCH_FAILED)
-
-#define CONCAT(a, b) a##_##b
-#define DEFER_LINE(a, line) CONCAT(a, line)
-#define TYPE_CHECK(expr, type) TYPE_CHECK_(expr, type, DEFER_LINE(tc, __LINE__))
-
-typedef enum CheckStatus {
-    Check_Success,  // The node was successfully checked with out errors
-    Check_Complete, // The node is done processing
-
-    Check_Errors_Start,
-    Check_Return_To_Symres, // Return this node for further symres processing
-    Check_Yield_Macro,
-    Check_Failed,           // The node is done processing and should be put in the state of Failed.
-    Check_Error,    // There was an error when checking the node
-} CheckStatus;
-
-CheckStatus check_block(AstBlock* block);
-CheckStatus check_statement_chain(AstNode** start);
-CheckStatus check_statement(AstNode** pstmt);
-CheckStatus check_return(AstReturn* retnode);
-CheckStatus check_if(AstIfWhile* ifnode);
-CheckStatus check_while(AstIfWhile* whilenode);
-CheckStatus check_for(AstFor* fornode);
-CheckStatus check_switch(AstSwitch* switchnode);
-CheckStatus check_call(AstCall** pcall);
-CheckStatus check_binaryop(AstBinaryOp** pbinop);
-CheckStatus check_unaryop(AstUnaryOp** punop);
-CheckStatus check_struct_literal(AstStructLiteral* sl);
-CheckStatus check_array_literal(AstArrayLiteral* al);
-CheckStatus check_range_literal(AstRangeLiteral** range);
-CheckStatus check_compound(AstCompound* compound);
-CheckStatus check_if_expression(AstIfExpression* if_expr);
-CheckStatus check_expression(AstTyped** expr);
-CheckStatus check_address_of(AstAddressOf** paof);
-CheckStatus check_dereference(AstDereference* deref);
-CheckStatus check_subscript(AstSubscript** paa);
-CheckStatus check_field_access(AstFieldAccess** pfield);
-CheckStatus check_method_call(AstBinaryOp** mcall);
-CheckStatus check_size_of(AstSizeOf* so);
-CheckStatus check_align_of(AstAlignOf* ao);
-CheckStatus check_global(AstGlobal* global);
-CheckStatus check_function(AstFunction* func);
-CheckStatus check_overloaded_function(AstOverloadedFunction* func);
-CheckStatus check_struct(AstStructType* s_node);
-CheckStatus check_temp_function_header(AstFunction* func);
-CheckStatus check_function_header(AstFunction* func);
-CheckStatus check_memres_type(AstMemRes* memres);
-CheckStatus check_memres(AstMemRes* memres);
-CheckStatus check_type(AstType** ptype);
-CheckStatus check_insert_directive(AstDirectiveInsert** pinsert);
-CheckStatus check_directive_solidify(AstDirectiveSolidify** psolid);
-CheckStatus check_do_block(AstDoBlock** pdoblock);
-CheckStatus check_constraint(AstConstraint *constraint);
-CheckStatus check_constraint_context(ConstraintContext *cc, Scope *scope, OnyxFilePos pos);
-CheckStatus check_polyquery(AstPolyQuery *query);
-
-// HACK HACK HACK
-b32 expression_types_must_be_known = 0;
-b32 all_checks_are_final           = 1;
-b32 inside_for_iterator            = 0;
-
-#define STATEMENT_LEVEL 1
-#define EXPRESSION_LEVEL 2
-u32 current_checking_level=0;
-
-static inline void fill_in_type(AstTyped* node) {
-    if (node->type == NULL) {
-        if (check_type(&node->type_node) > Check_Errors_Start) return;
-
-        node->type = type_build_from_ast(context.ast_alloc, node->type_node);
-    }
-}
-
-// HACK: This should be baked into a structure, not a global variable.
-static Type** expected_return_type = NULL;
-
-CheckStatus check_return(AstReturn* retnode) {
-    if (retnode->expr) {
-        CHECK(expression, &retnode->expr);
-
-        if (*expected_return_type == &type_auto_return) {
-            resolve_expression_type(retnode->expr);
-            if (retnode->expr->type == NULL)
-                YIELD_ERROR(retnode->token->pos, "Unable to determine the automatic return type here.");
-
-            *expected_return_type = retnode->expr->type;
-            return Check_Success;
-        }
-
-        TYPE_CHECK(&retnode->expr, *expected_return_type) {
-            ERROR_(retnode->token->pos,
-                    "Expected to return a value of type '%s', returning value of type '%s'.",
-                    type_get_name(*expected_return_type),
-                    node_get_type_name(retnode->expr));
-        }
-
-        //
-        // Catch the obvious case of return '^.{ ... }', as that will never
-        // be legal.
-        if (retnode->expr->kind == Ast_Kind_Address_Of) {
-            AstAddressOf *aof = (AstAddressOf *) retnode->expr;
-            if (node_is_addressable_literal((AstNode *) aof->expr)) {
-                ERROR(retnode->token->pos, "Cannot return a pointer to a literal, as the space reserved for the literal will be freed upon returning.");
-            }
-        }
-
-    } else {
-        if (*expected_return_type == &type_auto_return) {
-            *expected_return_type = &basic_types[Basic_Kind_Void];
-            return Check_Success;
-        }
-
-        if ((*expected_return_type)->Basic.size > 0) {
-            ERROR_(retnode->token->pos,
-                "Returning from non-void function without a value. Expected a value of type '%s'.",
-                type_get_name(*expected_return_type));
-        }
-    }
-
-    return Check_Success;
-}
-
-CheckStatus check_if(AstIfWhile* ifnode) {
-    if (ifnode->initialization != NULL) CHECK(statement_chain, &ifnode->initialization);
-
-    if (ifnode->kind == Ast_Kind_Static_If) {
-        if ((ifnode->flags & Ast_Flag_Static_If_Resolved) == 0) {
-            YIELD(ifnode->token->pos, "Waiting for static if to be resolved.");
-        }
-
-        if (static_if_resolution(ifnode)) {
-            if (ifnode->true_stmt != NULL) {
-                CHECK(statement, (AstNode **) &ifnode->true_stmt);
-                ifnode->true_stmt->rules = Block_Rule_Macro;
-            }
-
-        } else {
-            if (ifnode->false_stmt != NULL) {
-                CHECK(statement, (AstNode **) &ifnode->false_stmt);
-                ifnode->false_stmt->rules = Block_Rule_Macro;
-            }
-        }
-
-    } else {
-        CHECK(expression, &ifnode->cond);
-
-        if (!type_is_bool(ifnode->cond->type)) {
-            ERROR_(ifnode->cond->token->pos, "Expected expression of type 'bool' for condition, got '%s'", type_get_name(ifnode->cond->type));
-        }
-
-        if (ifnode->true_stmt)  CHECK(statement, (AstNode **) &ifnode->true_stmt);
-        if (ifnode->false_stmt) CHECK(statement, (AstNode **) &ifnode->false_stmt);
-    }
-
-    return Check_Success;
-}
-
-CheckStatus check_while(AstIfWhile* whilenode) {
-    if (whilenode->initialization != NULL) CHECK(statement_chain, &whilenode->initialization);
-
-    CHECK(expression, &whilenode->cond);
-
-    if (!type_is_bool(whilenode->cond->type)) {
-        ERROR_(whilenode->cond->token->pos, "Expected expression of type 'bool' for condition, got '%s'", type_get_name(whilenode->cond->type));
-    }
-
-    if (whilenode->true_stmt)  CHECK(statement, (AstNode **) &whilenode->true_stmt);
-    if (whilenode->false_stmt) {
-        if (whilenode->bottom_test) {
-            ERROR(whilenode->token->pos, "while-loops with an 'else' clause cannot be bottom tested.");
-        }
-
-        CHECK(statement, (AstNode **) &whilenode->false_stmt);
-    }
-
-    return Check_Success;
-}
-
-CheckStatus check_for(AstFor* fornode) {
-    b32 old_inside_for_iterator;
-    if (fornode->flags & Ast_Flag_Has_Been_Checked) goto fornode_expr_checked;
-
-    CHECK(expression, &fornode->iter);
-    resolve_expression_type(fornode->iter);
-
-    Type* iter_type = fornode->iter->type;
-    if (iter_type == NULL) YIELD(fornode->token->pos, "Waiting for iteration expression type to be known.");
-
-    OnyxFilePos error_loc = fornode->var->token->pos;
-    if (error_loc.filename == NULL) {
-        error_loc = fornode->token->pos;
-    }
-
-    fornode->loop_type = For_Loop_Invalid;
-    if (types_are_compatible(iter_type, &basic_types[Basic_Kind_I32])) {
-        if (fornode->by_pointer) {
-            ERROR(error_loc, "Cannot iterate by pointer over a range.");
-        }
-
-        AstNumLit* low_0    = make_int_literal(context.ast_alloc, 0);
-        AstRangeLiteral* rl = make_range_literal(context.ast_alloc, (AstTyped *) low_0, fornode->iter);
-        CHECK(range_literal, &rl);
-        fornode->iter = (AstTyped *) rl;
-
-        fornode->var->type = builtin_range_type_type->Struct.memarr[0]->type;
-        fornode->var->flags |= Ast_Flag_Cannot_Take_Addr;
-        fornode->loop_type = For_Loop_Range;
-    }
-    else if (types_are_compatible(iter_type, builtin_range_type_type)) {
-        if (fornode->by_pointer) {
-            ERROR(error_loc, "Cannot iterate by pointer over a range.");
-        }
-
-        // NOTE: Blindly copy the first range member's type which will
-        // be the low value.                - brendanfh 2020/09/04
-        fornode->var->type = builtin_range_type_type->Struct.memarr[0]->type;
-        fornode->var->flags |= Ast_Flag_Cannot_Take_Addr;
-        fornode->loop_type = For_Loop_Range;
-
-    }
-    else if (iter_type->kind == Type_Kind_Array) {
-        if (fornode->by_pointer) fornode->var->type = type_make_pointer(context.ast_alloc, iter_type->Array.elem);
-        else                     fornode->var->type = iter_type->Array.elem;
-
-        fornode->loop_type = For_Loop_Array;
-    }
-    else if (iter_type->kind == Type_Kind_Slice) {
-        if (fornode->by_pointer) fornode->var->type = type_make_pointer(context.ast_alloc, iter_type->Slice.elem);
-        else                     fornode->var->type = iter_type->Slice.elem;
-
-        fornode->loop_type = For_Loop_Slice;
-
-    }
-    else if (iter_type->kind == Type_Kind_VarArgs) {
-        if (fornode->by_pointer) {
-            ERROR_(error_loc, "Cannot iterate by pointer over '%s'.", type_get_name(iter_type));
-        }
-
-        fornode->var->type = iter_type->VarArgs.elem;
-
-        // NOTE: Slices are VarArgs are being treated the same here.
-        fornode->loop_type = For_Loop_Slice;
-    }
-    else if (iter_type->kind == Type_Kind_DynArray) {
-        if (fornode->by_pointer) fornode->var->type = type_make_pointer(context.ast_alloc, iter_type->DynArray.elem);
-        else                     fornode->var->type = iter_type->DynArray.elem;
-
-        fornode->loop_type = For_Loop_DynArr;
-    }
-    else if (type_struct_constructed_from_poly_struct(iter_type, builtin_iterator_type)) {
-        if (fornode->by_pointer) {
-            ERROR(error_loc, "Cannot iterate by pointer over an iterator.");
-        }
-
-        // HACK: This assumes the Iterator type only has a single type argument.
-        fornode->var->type = iter_type->Struct.poly_sln[0].type;
-        fornode->loop_type = For_Loop_Iterator;
-    }
-
-    if (fornode->by_pointer)
-        fornode->var->flags |= Ast_Flag_Cannot_Take_Addr;
-
-    if (fornode->loop_type == For_Loop_Invalid)
-        ERROR_(error_loc, "Cannot iterate over a '%s'.", type_get_name(iter_type));
-
-    if (fornode->no_close && fornode->loop_type != For_Loop_Iterator) {
-        onyx_report_warning(error_loc, "Warning: #no_close here is meaningless as the iterable is not an iterator.");
-    }
-
-    fornode->flags |= Ast_Flag_Has_Been_Checked;
-
-fornode_expr_checked:
-    old_inside_for_iterator = inside_for_iterator;
-    inside_for_iterator = 0;
-    iter_type = fornode->iter->type;
-    if (type_struct_constructed_from_poly_struct(iter_type, builtin_iterator_type)) {
-        inside_for_iterator = 1;
-    }
-
-    do {
-        CheckStatus cs = check_block(fornode->stmt);
-        inside_for_iterator = old_inside_for_iterator;
-        if (cs > Check_Errors_Start) return cs; 
-    } while(0);
-
-    return Check_Success;
-}
-
-static b32 add_case_to_switch_statement(AstSwitch* switchnode, u64 case_value, AstBlock* block, OnyxFilePos pos) {
-    assert(switchnode->switch_kind == Switch_Kind_Integer);
-
-    switchnode->min_case = bh_min(switchnode->min_case, case_value);
-    switchnode->max_case = bh_max(switchnode->max_case, case_value);
-
-    if (bh_imap_has(&switchnode->case_map, case_value)) {
-        onyx_report_error(pos, Error_Critical, "Multiple cases for values '%d'.", case_value);
-        return 1;
-    }
-
-    bh_imap_put(&switchnode->case_map, case_value, (u64) block);
-    return 0;
-}
-
-static CheckStatus collect_switch_case_blocks(AstSwitch* switchnode, AstBlock* root) {
-    AstNode *walker = root->body;
-    while (walker != NULL) {
-        switch (walker->kind) {
-            case Ast_Kind_Block:
-                collect_switch_case_blocks(switchnode, (AstBlock *) walker);
-                break;
-
-            case Ast_Kind_Switch_Case: {
-                AstSwitchCase *case_node = (AstSwitchCase *) walker;
-                if (case_node->is_default) {
-                    if (switchnode->default_case != NULL && switchnode->default_case != case_node->block) {
-                        ERROR(case_node->token->pos, "Multiple #default cases given");
-                        ERROR(switchnode->default_case->token->pos, "Multiple #default cases given");
-                        return Check_Error;
-                    }
-
-                    switchnode->default_case = case_node->block;
-                } else {
-                    bh_arr_push(switchnode->cases, case_node);
-                }
-                break;
-            }
-
-            default:
-                ERROR(walker->token->pos, "This statement is not allowed here.");
-        }
-
-        walker = walker->next;
-    }
-
-    return Check_Success;
-}
-
-CheckStatus check_switch(AstSwitch* switchnode) {
-    if (switchnode->initialization != NULL) CHECK(statement_chain, &switchnode->initialization);
-
-    CHECK(expression, &switchnode->expr);
-    Type* resolved_expr_type = resolve_expression_type(switchnode->expr);
-
-    if (!(switchnode->flags & Ast_Flag_Has_Been_Checked)) {
-        if (resolved_expr_type == NULL) YIELD(switchnode->token->pos, "Waiting for expression type to be known.");
-
-        switchnode->switch_kind = Switch_Kind_Integer;
-        if (!type_is_integer(switchnode->expr->type) && switchnode->expr->type->kind != Type_Kind_Enum) {
-            switchnode->switch_kind = Switch_Kind_Use_Equals;
-        }
-
-        switch (switchnode->switch_kind) {
-            case Switch_Kind_Integer:
-                switchnode->min_case = 0xffffffffffffffff;
-                bh_imap_init(&switchnode->case_map, global_heap_allocator, 4);
-                break;
-
-            case Switch_Kind_Use_Equals:
-                bh_arr_new(global_heap_allocator, switchnode->case_exprs, 4);
-                break;
-
-            default: assert(0);
-        }
-    }
-    switchnode->flags |= Ast_Flag_Has_Been_Checked;
-
-    // Should the case block code be checked here?
-    // Or should this just exist to resolve macros and expand #unquotes
-    // then the cases are consumed into the array or cases, THEN the blocks
-    // are actually checked?
-    if (switchnode->cases == NULL) {
-        CHECK(block, switchnode->case_block);
-
-        bh_arr_new(global_heap_allocator, switchnode->cases, 4);
-        if (collect_switch_case_blocks(switchnode, switchnode->case_block) != Check_Success) {
-            return Check_Error;
-        }
-
-        // This is important, otherwise if this block has to return to symbol resolution.
-        switchnode->case_block->statement_idx = 0;
-    }
-
-    fori (i, switchnode->yield_return_index, bh_arr_length(switchnode->cases)) {
-        AstSwitchCase *sc = switchnode->cases[i];
-        CHECK(block, sc->block);
-
-        bh_arr_each(AstTyped *, value, sc->values) {
-            CHECK(expression, value);
-
-            if (switchnode->switch_kind == Switch_Kind_Integer && (*value)->kind == Ast_Kind_Range_Literal) {
-                AstRangeLiteral* rl = (AstRangeLiteral *) (*value);
-                resolve_expression_type(rl->low);
-                resolve_expression_type(rl->high);
-
-                if (rl->low->kind != Ast_Kind_NumLit || rl->high->kind != Ast_Kind_NumLit) {
-                    ERROR(rl->token->pos, "case statement expected compile time known range.");
-                }
-
-                promote_numlit_to_larger((AstNumLit *) rl->low);
-                promote_numlit_to_larger((AstNumLit *) rl->high);
-
-                i64 lower = ((AstNumLit *) rl->low)->value.l;
-                i64 upper = ((AstNumLit *) rl->high)->value.l;
-
-                // NOTE: This is inclusive!!!!
-                fori (case_value, lower, upper + 1) {
-                    if (add_case_to_switch_statement(switchnode, case_value, sc->block, rl->token->pos))
-                        return Check_Error;
-                }
-
-                continue;
-            }
-
-            TYPE_CHECK(value, resolved_expr_type) {
-                OnyxToken* tkn = sc->block->token;
-                if ((*value)->token) tkn = (*value)->token;
-
-                ERROR_(tkn->pos, "Mismatched types in switch-case. Expected '%s', got '%s'.",
-                    type_get_name(resolved_expr_type), type_get_name((*value)->type));
-            }
-
-            switch (switchnode->switch_kind) {
-                case Switch_Kind_Integer: {
-                    b32 is_valid;
-                    i64 integer_value = get_expression_integer_value(*value, &is_valid);
-                    if (!is_valid)
-                        ERROR_((*value)->token->pos, "Case statement expected compile time known integer. Got '%s'.", onyx_ast_node_kind_string((*value)->kind));
-
-                    if (add_case_to_switch_statement(switchnode, integer_value, sc->block, sc->block->token->pos))
-                        return Check_Error;
-
-                    break;
-                }
-
-                case Switch_Kind_Use_Equals: {
-                    // Gross
-                    b32 found = 0;
-                    bh_arr_each(CaseToBlock, ctb, switchnode->case_exprs) {
-                        if (ctb->original_value == *value) {
-                            CHECK(expression, (AstTyped **) &ctb->comparison);
-                            found = 1;
-                            break;
-                        }
-                    }
-                    if (found) break;
-
-                    CaseToBlock ctb;
-                    ctb.block = sc->block;
-                    ctb.original_value = *value;
-                    ctb.comparison = make_binary_op(context.ast_alloc, Binary_Op_Equal, switchnode->expr, *value);
-                    ctb.comparison->token = (*value)->token;
-                    bh_arr_push(switchnode->case_exprs, ctb);
-
-                    CHECK(binaryop, &bh_arr_last(switchnode->case_exprs).comparison);
-                    break;
-                }
-            }
-        }
-
-        switchnode->yield_return_index += 1;
-    }
-
-    if (switchnode->default_case)
-        CHECK(block, switchnode->default_case);
-
-    return 0;
-}
-
-CheckStatus check_arguments(Arguments* args) {
-    bh_arr_each(AstTyped *, actual, args->values)
-        CHECK(expression, actual);
-
-    bh_arr_each(AstNamedValue *, named_value, args->named_values)
-        CHECK(expression, &(*named_value)->value);
-
-    return Check_Success;
-}
-
-CheckStatus check_argument(AstArgument** parg) {
-    CHECK(expression, &(*parg)->value);
-    (*parg)->type = (*parg)->value->type;
-
-    return Check_Success;
-}
-
-static CheckStatus check_resolve_callee(AstCall* call, AstTyped** effective_callee) {
-    if (call->kind == Ast_Kind_Intrinsic_Call) return Check_Success;
-
-    AstTyped* callee = (AstTyped *) strip_aliases((AstNode *) call->callee);
-    b32 calling_a_macro = 0;
-
-    if (callee->kind == Ast_Kind_Overloaded_Function) {
-        AstTyped* new_callee = find_matching_overload_by_arguments(
-            ((AstOverloadedFunction *) callee)->overloads,
-            &call->args);
-
-        if (new_callee == NULL) {
-            report_unable_to_match_overload(call, ((AstOverloadedFunction *) callee)->overloads);
-            return Check_Error;
-        }
-
-        if (new_callee == (AstTyped *) &node_that_signals_a_yield) {
-            YIELD(call->token->pos, "Waiting for overloaded function option to pass type-checking.");
-        }
-
-        callee = new_callee;
-    }
-
-    if (callee->kind == Ast_Kind_Macro) {
-        calling_a_macro = 1;
-        call->callee = callee;
-
-        AstTyped* new_callee = (AstTyped *) macro_resolve_header((AstMacro *) callee, &call->args, call->token, 1);
-        if (new_callee == NULL) return Check_Error;
-        if (new_callee == (AstTyped *) &node_that_signals_a_yield) {
-            YIELD(call->token->pos, "Waiting for macro header to pass type-checking.");
-        }
-
-        arguments_remove_baked(&call->args);
-        callee = new_callee;
-
-    } else while (callee->kind == Ast_Kind_Polymorphic_Proc) {
-        AstTyped* new_callee = (AstTyped *) polymorphic_proc_lookup((AstFunction *) callee, PPLM_By_Arguments, &call->args, call->token);
-        if (new_callee == NULL) return Check_Error;
-        if (new_callee == (AstTyped *) &node_that_signals_a_yield) {
-            YIELD(call->token->pos, "Waiting for polymorphic procedure header to pass type-checking.");
-        }
-
-        arguments_remove_baked(&call->args);
-        callee = new_callee;
-    }
-
-    // NOTE: Build callee's type
-    fill_in_type((AstTyped *) callee);
-    if (callee->type == NULL) {
-        YIELD(call->token->pos, "Trying to resolve function type for callee.");
-    }
-
-    if (!calling_a_macro) call->callee = callee;
-
-    if (callee->type->kind != Type_Kind_Function) {
-        ERROR_(call->token->pos,
-                "Attempting to call something that is not a function, '%b'.",
-                callee->token->text, callee->token->length);
-    }
-
-    *effective_callee = callee;
-    return Check_Success;
-}
-
-CheckStatus check_call(AstCall** pcall) {
-    // All the things that need to be done when checking a call node.
-    //      1. Ensure the callee is not a symbol
-    //      2. Check the callee expression (since it could be a variable or a field access, etc)
-    //      3. Check all arguments
-    //          * Cannot pass overloaded functions (ROBUSTNESS)
-    //      4. If callee is an overloaded function, use the argument types to determine which overload is used.
-    //      5. If callee is polymorphic, use the arguments type to generate a polymorphic function.
-    //      7. Fill in arguments
-    //      8. If callee is an intrinsic, turn call into an Intrinsic_Call node
-    //      9. Check types of formal and actual params against each other, handling varargs
-    AstCall* call = *pcall;
-
-    if (call->kind == Ast_Kind_Call) {
-        AstNode* callee = strip_aliases((AstNode *) call->callee);
-        if (callee->kind == Ast_Kind_Poly_Struct_Type) {
-            *pcall = (AstCall *) convert_call_to_polycall(call);
-            CHECK(expression, (AstTyped **) pcall);
-            return Check_Success;
-        }
-    }
-
-    if (call->flags & Ast_Flag_Has_Been_Checked) return Check_Success;
-
-    u32 current_checking_level_store = current_checking_level;
-    CHECK(expression, &call->callee);
-    CHECK(arguments, &call->args);
-    current_checking_level = current_checking_level_store;
-
-    AstFunction* callee=NULL;
-    CHECK(resolve_callee, call, (AstTyped **) &callee);
-
-    if (callee->kind == Ast_Kind_Function) {
-        if (callee->constraints.constraints != NULL && callee->constraints.constraints_met == 0) {
-            YIELD(call->token->pos, "Waiting for constraints to be checked on callee.");
-        }
-    }
-
-    i32 arg_count = get_argument_buffer_size(&callee->type->Function, &call->args);
-    arguments_ensure_length(&call->args, arg_count);
-
-    char* err_msg = NULL;
-    fill_in_arguments(&call->args, (AstNode *) callee, &err_msg, 0);
-    if (err_msg != NULL) ERROR(call->token->pos, err_msg);
-
-    bh_arr(AstArgument *) arg_arr = (bh_arr(AstArgument *)) call->args.values;
-    bh_arr_each(AstArgument *, arg, arg_arr) {
-        if (*arg != NULL) continue;
-
-        ERROR(call->token->pos, "Not all arguments were given a value.");
-    }
-
-    // HACK HACK HACK
-    // :CallSiteIsGross
-    bh_arr_each(AstArgument *, arg, arg_arr) {
-        AstTyped** arg_value = &(*arg)->value;
-
-        if ((*arg_value)->kind == Ast_Kind_Call_Site) {
-            AstCallSite* callsite = (AstCallSite *) ast_clone(context.ast_alloc, *arg_value);
-            callsite->callsite_token = call->token;
-
-            // HACK CLEANUP
-            OnyxToken* str_token = bh_alloc(context.ast_alloc, sizeof(OnyxToken));
-            str_token->text  = bh_strdup(global_heap_allocator, (char *) call->token->pos.filename);
-            str_token->length = strlen(call->token->pos.filename);
-            str_token->pos = call->token->pos;
-            str_token->type = Token_Type_Literal_String;
-
-            AstStrLit* filename = bh_alloc_item(context.ast_alloc, AstStrLit);
-            memset(filename, 0, sizeof(AstStrLit));
-            filename->kind  = Ast_Kind_StrLit;
-            filename->token = str_token;
-            filename->data_id = 0;
-
-            add_entities_for_node(NULL, (AstNode *) filename, NULL, NULL);
-            callsite->filename = filename;
-
-            callsite->line   = make_int_literal(context.ast_alloc, call->token->pos.line);
-            callsite->column = make_int_literal(context.ast_alloc, call->token->pos.column);
-
-            convert_numlit_to_type(callsite->line,   &basic_types[Basic_Kind_U32]);
-            convert_numlit_to_type(callsite->column, &basic_types[Basic_Kind_U32]);
-
-            *arg_value = (AstTyped *) callsite;
-        }
-    }
-
-    // NOTE: If we are calling an intrinsic function, translate the
-    // call into an intrinsic call node.
-    if (callee->kind == Ast_Kind_Function && callee->is_intrinsic) {
-        call->kind = Ast_Kind_Intrinsic_Call;
-        call->callee = NULL;
-
-        token_toggle_end(callee->intrinsic_name);
-        char* intr_name = callee->intrinsic_name->text;
-
-        i32 index;
-        if ((index = shgeti(intrinsic_table, intr_name)) == -1) {
-            onyx_report_error(callee->token->pos, Error_Critical, "Intrinsic not supported, '%s'.", intr_name);
-            token_toggle_end(callee->intrinsic_name);
-            return Check_Error;
-        }
-
-        call->intrinsic = intrinsic_table[index].value;
-
-        token_toggle_end(callee->intrinsic_name);
-    }
-
-    call->va_kind = VA_Kind_Not_VA;
-    call->type = callee->type->Function.return_type;
-    if (call->type == &type_auto_return && call->callee->kind != Ast_Kind_Macro) {
-        YIELD(call->token->pos, "Waiting for auto-return type to be solved.");
-    }
-
-    OnyxError error;
-    TypeMatch tm = check_arguments_against_type(&call->args, &callee->type->Function, &call->va_kind,
-                                                call->token, get_function_name(callee), &error);
-    if (tm == TYPE_MATCH_FAILED) {
-        onyx_submit_error(error);
-        return Check_Error;
-    }
-
-    if (tm == TYPE_MATCH_YIELD) YIELD(call->token->pos, "Waiting on argument type checking.");
-
-    call->flags   |= Ast_Flag_Has_Been_Checked;
-    callee->flags |= Ast_Flag_Function_Used;
-
-    if (call->kind == Ast_Kind_Call && call->callee->kind == Ast_Kind_Macro) {
-        expand_macro(pcall, callee);
-        return Check_Return_To_Symres;
-    }
-
-    return Check_Success;
-}
-
-static void report_bad_binaryop(AstBinaryOp* binop) {
-    onyx_report_error(binop->token->pos, Error_Critical, "Binary operator '%s' not understood for arguments of type '%s' and '%s'.",
-            binaryop_string[binop->operation],
-            node_get_type_name(binop->left),
-            node_get_type_name(binop->right));
-}
-
-static AstCall* binaryop_try_operator_overload(AstBinaryOp* binop, AstTyped* third_argument) {
-    if (bh_arr_length(operator_overloads[binop->operation]) == 0) return NULL;
-
-    if (binop->overload_args == NULL) {
-        binop->overload_args = bh_alloc_item(context.ast_alloc, Arguments);
-        bh_arr_new(context.ast_alloc, binop->overload_args->values, 3);
-        bh_arr_set_length(binop->overload_args->values, third_argument ? 3 : 2);
-
-        if (binop_is_assignment(binop->operation)) {
-            binop->overload_args->values[0] = (AstTyped *) make_address_of(context.ast_alloc, binop->left);
-
-            u32 current_all_checks_are_final = all_checks_are_final;
-            all_checks_are_final = 0;
-            u32 current_checking_level_store = current_checking_level;
-            CheckStatus cs = check_address_of((AstAddressOf **) &binop->overload_args->values[0]);
-            current_checking_level = current_checking_level_store;
-            all_checks_are_final   = current_all_checks_are_final;
-
-            if (cs == Check_Yield_Macro)      return (AstCall *) &node_that_signals_a_yield;
-            if (cs == Check_Error)            return NULL;
-
-            binop->overload_args->values[0] = (AstTyped *) make_argument(context.ast_alloc, binop->overload_args->values[0]);
-
-        } else {
-            binop->overload_args->values[0] = (AstTyped *) make_argument(context.ast_alloc, binop->left);
-        }
-
-
-        binop->overload_args->values[1] = (AstTyped *) make_argument(context.ast_alloc, binop->right);
-        if (third_argument != NULL) binop->overload_args->values[2] = (AstTyped *) make_argument(context.ast_alloc, third_argument);
-    }
-
-    AstTyped* overload = find_matching_overload_by_arguments(operator_overloads[binop->operation], binop->overload_args);
-    if (overload == NULL || overload == (AstTyped *) &node_that_signals_a_yield) return (AstCall *) overload;
-
-    AstCall* implicit_call = onyx_ast_node_new(context.ast_alloc, sizeof(AstCall), Ast_Kind_Call);
-    implicit_call->token = binop->token;
-    implicit_call->callee = overload;
-    implicit_call->va_kind = VA_Kind_Not_VA;
-
-    arguments_clone(&implicit_call->args, binop->overload_args);
-    return implicit_call;
-}
-
-
-CheckStatus check_binaryop_assignment(AstBinaryOp** pbinop) {
-    AstBinaryOp* binop = *pbinop;
-    if (current_checking_level == EXPRESSION_LEVEL)
-        ERROR(binop->token->pos, "Assignment not valid in expression.");
-
-    if (!is_lval((AstNode *) binop->left))
-        ERROR_(binop->left->token->pos,
-                "Cannot assign to '%b'.",
-                binop->left->token->text, binop->left->token->length);
-
-    if ((binop->left->flags & Ast_Flag_Const) != 0 && binop->left->type != NULL)
-        ERROR_(binop->token->pos,
-                "Cannot assign to constant '%b'.",
-                binop->left->token->text, binop->left->token->length);
-
-    if (binop->operation == Binary_Op_Assign) {
-        // NOTE: Raw assignment
-
-        // NOTE: This is the 'type inference' system. Very stupid, but very easy.
-        // If a left operand has an unknown type, fill it in with the type of
-        // the right hand side.
-        if (binop->left->type == NULL) {
-            if (binop->left->type_node != NULL && binop->left->entity && binop->left->entity->state <= Entity_State_Check_Types) {
-                YIELD(binop->token->pos, "Waiting for type to be constructed on left hand side.");
-            }
-
-            // NOTE: There is a subtlety here. You cannot use the result of `resolve_expression_type` directly,
-            // as in some cases (especially with macros and polyprocs), the result is not "correct". The result
-            // makes them appears as though they are runtime-known values, which they are not. Using the following
-            // pattern does prevent this issue.
-            resolve_expression_type(binop->right);
-
-            Type* right_type = get_expression_type(binop->right);
-            if (right_type == NULL) {
-                if (binop->right->entity == NULL || binop->right->entity->state > Entity_State_Check_Types) {
-                    ERROR(binop->token->pos, "Could not resolve type of right hand side to infer.");
-
-                } else {
-                    YIELD(binop->token->pos, "Trying to resolve try of right hand side.");
-                }
-            }
-
-            if (right_type->kind == Type_Kind_Compound) {
-                AstCompound* lhs = (AstCompound *) binop->left;
-                i32 expr_count = right_type->Compound.count;
-                if (lhs->kind != Ast_Kind_Compound || bh_arr_length(lhs->exprs) != expr_count) {
-                    ERROR_(binop->token->pos, "Expected left hand side to have %d expressions.", expr_count);
-                }
-
-                fori (i, 0, expr_count) lhs->exprs[i]->type = right_type->Compound.types[i];
-
-                lhs->type = type_build_compound_type(context.ast_alloc, lhs);
-
-            } else {
-                binop->left->type = right_type;
-            }
-        }
-
-    } else {
-        // NOTE: +=, -=, ...
-        // NOTE: At this point, it is assumed that operator overloads for +=, -=, etc have been tested.
-
-        BinaryOp operation = -1;
-        if      (binop->operation == Binary_Op_Assign_Add)      operation = Binary_Op_Add;
-        else if (binop->operation == Binary_Op_Assign_Minus)    operation = Binary_Op_Minus;
-        else if (binop->operation == Binary_Op_Assign_Multiply) operation = Binary_Op_Multiply;
-        else if (binop->operation == Binary_Op_Assign_Divide)   operation = Binary_Op_Divide;
-        else if (binop->operation == Binary_Op_Assign_Modulus)  operation = Binary_Op_Modulus;
-        else if (binop->operation == Binary_Op_Assign_And)      operation = Binary_Op_And;
-        else if (binop->operation == Binary_Op_Assign_Or)       operation = Binary_Op_Or;
-        else if (binop->operation == Binary_Op_Assign_Xor)      operation = Binary_Op_Xor;
-        else if (binop->operation == Binary_Op_Assign_Shl)      operation = Binary_Op_Shl;
-        else if (binop->operation == Binary_Op_Assign_Shr)      operation = Binary_Op_Shr;
-        else if (binop->operation == Binary_Op_Assign_Sar)      operation = Binary_Op_Sar;
-
-        AstBinaryOp* new_right = make_binary_op(context.ast_alloc, operation, binop->left, binop->right);
-        binop->right = (AstTyped *) new_right;
-        new_right->token = binop->token;
-        binop->operation = Binary_Op_Assign;
-
-        CHECK(binaryop, (AstBinaryOp **) &binop->right);
-    }
-
-    if (binop->right->type == NULL) {
-        if (binop->right->entity != NULL && binop->right->entity->state <= Entity_State_Check_Types) {
-            YIELD(binop->token->pos, "Trying to resolve type of right hand side.");
-        }
-    }
-
-    TYPE_CHECK(&binop->right, binop->left->type) {
-        ERROR_(binop->token->pos,
-                "Cannot assign value of type '%s' to a '%s'.",
-                node_get_type_name(binop->right),
-                node_get_type_name(binop->left));
-    }
-
-    binop->type = &basic_types[Basic_Kind_Void];
-
-    return Check_Success;
-}
-
-static b32 binary_op_is_allowed(BinaryOp operation, Type* type) {
-    static const u8 binop_allowed[Binary_Op_Count] = {
-        /* Add */             Basic_Flag_Numeric | Basic_Flag_Pointer,
-        /* Minus */           Basic_Flag_Numeric | Basic_Flag_Pointer,
-        /* Multiply */        Basic_Flag_Numeric,
-        /* Divide */          Basic_Flag_Numeric,
-        /* Modulus */         Basic_Flag_Integer,
-
-        /* Equal */           Basic_Flag_Equality,
-        /* Not_Equal */       Basic_Flag_Equality,
-        /* Less */            Basic_Flag_Ordered,
-        /* Less_Equal */      Basic_Flag_Ordered,
-        /* Greater */         Basic_Flag_Ordered,
-        /* Greater_Equal */   Basic_Flag_Ordered,
-
-        /* And */             Basic_Flag_Integer,
-        /* Or */              Basic_Flag_Integer,
-        /* Xor */             Basic_Flag_Integer,
-        /* Shl */             Basic_Flag_Integer,
-        /* Shr */             Basic_Flag_Integer,
-        /* Sar */             Basic_Flag_Integer,
-
-        /* Bool_And */        Basic_Flag_Boolean,
-        /* Bool_Or */         Basic_Flag_Boolean,
-
-        /* Assign_Start */    0,
-        /* Assign */          0,
-        /* Assign_Add */      0,
-        /* Assign_Minus */    0,
-        /* Assign_Multiply */ 0,
-        /* Assign_Divide */   0,
-        /* Assign_Modulus */  0,
-        /* Assign_And */      0,
-        /* Assign_Or */       0,
-        /* Assign_Xor */      0,
-        /* Assign_Shl */      0,
-        /* Assign_Shr */      0,
-        /* Assign_Sar */      0,
-        /* Assign_End */      0,
-
-        /* Pipe */            0,
-        /* Range */           0,
-    };
-
-    enum BasicFlag effective_flags = 0;
-    switch (type->kind) {
-        case Type_Kind_Basic:    effective_flags = type->Basic.flags;  break;
-        case Type_Kind_Pointer:  effective_flags = Basic_Flag_Pointer; break;
-        case Type_Kind_Enum:     effective_flags = Basic_Flag_Integer; break;
-        case Type_Kind_Function: effective_flags = Basic_Flag_Equality; break;
-    }
-
-    return (binop_allowed[operation] & effective_flags) != 0;
-}
-
-CheckStatus check_binaryop_compare(AstBinaryOp** pbinop) {
-    AstBinaryOp* binop = *pbinop;
-
-    // HACK: Since ^... to rawptr is a one way conversion, strip any pointers
-    // away so they can be compared as expected
-    Type* ltype = binop->left->type;
-    Type* rtype = binop->right->type;
-
-    if (ltype == NULL) YIELD(binop->token->pos, "Waiting for left-type to be known.");
-    if (rtype == NULL) YIELD(binop->token->pos, "Waiting for right-type to be known.");
-
-    if (ltype->kind == Type_Kind_Pointer) ltype = &basic_types[Basic_Kind_Rawptr];
-    if (rtype->kind == Type_Kind_Pointer) rtype = &basic_types[Basic_Kind_Rawptr];
-
-    if (!types_are_compatible(ltype, rtype)) {
-        b32 left_ac  = node_is_auto_cast((AstNode *) binop->left);
-        b32 right_ac = node_is_auto_cast((AstNode *) binop->right);
-        if (left_ac && right_ac) ERROR(binop->token->pos, "Cannot have auto cast on both sides of binary operator.");
-
-        TYPE_CHECK(&binop->left, rtype) {
-            TYPE_CHECK(&binop->right, ltype) {
-                ERROR_(binop->token->pos,
-                        "Cannot compare '%s' to '%s'.",
-                        type_get_name(binop->left->type),
-                        type_get_name(binop->right->type));
-            }
-        }
-    }
-
-    if (!binary_op_is_allowed(binop->operation, binop->left->type)) {
-        report_bad_binaryop(binop);
-        return Check_Error;
-    }
-
-    binop->type = &basic_types[Basic_Kind_Bool];
-    if (binop->flags & Ast_Flag_Comptime) {
-        // NOTE: Not a binary op
-        *pbinop = (AstBinaryOp *) ast_reduce(context.ast_alloc, (AstTyped *) binop);
-    }
-
-    return Check_Success;
-}
-
-CheckStatus check_binaryop_bool(AstBinaryOp** pbinop) {
-    AstBinaryOp* binop = *pbinop;
-
-    if (!type_is_bool(binop->left->type) || !type_is_bool(binop->right->type)) {
-        report_bad_binaryop(binop);
-        return Check_Error;
-    }
-
-    binop->type = &basic_types[Basic_Kind_Bool];
-
-    if (binop->flags & Ast_Flag_Comptime) {
-        // NOTE: Not a binary op
-        *pbinop = (AstBinaryOp *) ast_reduce(context.ast_alloc, (AstTyped *) binop);
-    }
-    return Check_Success;
-}
-
-CheckStatus check_binaryop(AstBinaryOp** pbinop) {
-    AstBinaryOp* binop = *pbinop;
-
-    if (binop->flags & Ast_Flag_Has_Been_Checked) return Check_Success;
-
-    if (binop->operation == Binary_Op_Assign && binop->left->kind == Ast_Kind_Subscript && bh_arr_length(operator_overloads[Binary_Op_Subscript_Equals]) > 0) {
-        AstSubscript* sub = (AstSubscript *) binop->left;
-
-        if (binop->potential_substitute == NULL) {
-            u32 current_checking_level_store = current_checking_level;
-            CHECK(expression, &sub->addr);
-            CHECK(expression, &sub->expr);
-            CHECK(expression, &binop->right);
-            current_checking_level = current_checking_level_store;
-
-            AstBinaryOp *op = onyx_ast_node_new(context.ast_alloc, sizeof(AstBinaryOp), Ast_Kind_Binary_Op);
-            op->token = binop->token;
-            op->operation = Binary_Op_Subscript_Equals;
-            op->left  = ((AstSubscript *) binop->left)->addr;
-            op->right = ((AstSubscript *) binop->left)->expr;
-
-            binop->potential_substitute = op;
-        }
-
-        AstCall* call = binaryop_try_operator_overload(binop->potential_substitute, binop->right);
-        if (call == (AstCall *) &node_that_signals_a_yield) YIELD(binop->token->pos, "Waiting on potential operator overload.");
-        if (call != NULL) {
-            call->next = binop->next;
-            *(AstCall **) pbinop = call;
-
-            CHECK(call, (AstCall **) pbinop);
-            return Check_Success;
-        }
-
-    }
-
-    u32 current_checking_level_store = current_checking_level;
-    CHECK(expression, &binop->left);
-    CHECK(expression, &binop->right);
-    current_checking_level = current_checking_level_store;
-
-    // :UnaryFieldAccessIsGross
-    if (binop->left->kind == Ast_Kind_Unary_Field_Access || binop->right->kind == Ast_Kind_Unary_Field_Access) {
-        TYPE_CHECK(&binop->left, binop->right->type) {
-            TYPE_CHECK(&binop->right, binop->left->type) {
-                // TODO: This should report a better error about the Unary_Field_Access not be able to be resolved given whatever type.
-                //                                                                        - brendanfh 2021/12/31
-                report_bad_binaryop(binop);
-                return Check_Error;
-            }
-        }
-    }
-
-    if ((binop->left->flags & Ast_Flag_Comptime) && (binop->right->flags & Ast_Flag_Comptime)) {
-        binop->flags |= Ast_Flag_Comptime;
-    }
-
-    if (expression_types_must_be_known) {
-        if (binop->left->type == NULL || binop->right->type == NULL) {
-            ERROR(binop->token->pos, "Internal compiler error: one of the operands types is unknown here.");
-        }
-    }
-
-    // NOTE: Try operator overloading before checking everything else.
-    if ((binop->left->type != NULL && (binop->left->type->kind != Type_Kind_Basic || (binop->left->type->Basic.flags & Basic_Flag_SIMD) != 0))
-        || (binop->right->type != NULL && (binop->right->type->kind != Type_Kind_Basic || (binop->right->type->Basic.flags & Basic_Flag_SIMD) != 0))) {
-        AstCall *implicit_call = binaryop_try_operator_overload(binop, NULL);
-
-        if (implicit_call == (AstCall *) &node_that_signals_a_yield)
-            YIELD(binop->token->pos, "Trying to resolve operator overload.");
-
-        if (implicit_call != NULL) {
-            // NOTE: Not a binary op
-            implicit_call->next = binop->next;
-            *pbinop = (AstBinaryOp *) implicit_call;
-
-            CHECK(call, (AstCall **) pbinop);
-            return Check_Success;
-        }
-    }
-
-    if (binop_is_assignment(binop->operation)) return check_binaryop_assignment(pbinop);
-
-    if (binop->left->type == NULL && binop->left->entity && binop->left->entity->state <= Entity_State_Check_Types) {
-        YIELD(binop->left->token->pos, "Waiting for this type to be known");
-    }
-    if (binop->right->type == NULL && binop->right->entity && binop->right->entity->state <= Entity_State_Check_Types) {
-        YIELD(binop->right->token->pos, "Waiting for this type to be known");
-    }
-
-    // NOTE: Comparision operators and boolean operators are handled separately.
-    if (binop_is_compare(binop->operation))
-        return check_binaryop_compare(pbinop);
-    if (binop->operation == Binary_Op_Bool_And || binop->operation == Binary_Op_Bool_Or)
-        return check_binaryop_bool(pbinop);
-
-    // NOTE: The left side cannot be compound.
-    //       The right side always is numeric.
-    //       The left side cannot be rawptr.
-    if (type_is_compound(binop->left->type))  goto bad_binaryop;
-    if (!type_is_numeric(binop->right->type)) goto bad_binaryop;
-    if (type_is_rawptr(binop->left->type)) {
-        ERROR(binop->token->pos, "Cannot operate on a 'rawptr'. Cast it to a another pointer type first.");
-    }
-
-    // NOTE: Handle basic pointer math.
-    if (type_is_pointer(binop->left->type)) {
-        if (binop->operation != Binary_Op_Add && binop->operation != Binary_Op_Minus) goto bad_binaryop;
-
-        resolve_expression_type(binop->right);
-        if (!type_is_integer(binop->right->type)) goto bad_binaryop;
-
-        AstNumLit* numlit = make_int_literal(context.ast_alloc, type_size_of(binop->left->type->Pointer.elem));
-        numlit->token = binop->right->token;
-        numlit->type = binop->right->type;
-
-        AstBinaryOp* binop_node = make_binary_op(context.ast_alloc, Binary_Op_Multiply, binop->right, (AstTyped *) numlit);
-        binop_node->token = binop->token;
-        CHECK(binaryop, &binop_node);
-
-        binop->right = (AstTyped *) binop_node;
-        binop->type = binop->left->type;
-        binop->right->type = binop->left->type;
-    }
-
-    if (!types_are_compatible(binop->left->type, binop->right->type)) {
-        b32 left_ac  = node_is_auto_cast((AstNode *) binop->left);
-        b32 right_ac = node_is_auto_cast((AstNode *) binop->right);
-        if (left_ac && right_ac) {
-            ERROR(binop->token->pos, "Cannot have auto cast on both sides of binary operator.");
-        }
-
-        TYPE_CHECK(&binop->left, binop->right->type) {
-            TYPE_CHECK(&binop->right, binop->left->type) {
-                ERROR_(binop->token->pos,
-                        "Mismatched types for binary operation '%s'. left: '%s', right: '%s'.",
-                        binaryop_string[binop->operation],
-                        node_get_type_name(binop->left),
-                        node_get_type_name(binop->right));
-            }
-        }
-    }
-
-    binop->type = binop->left->type;
-    if (!binary_op_is_allowed(binop->operation, binop->type)) goto bad_binaryop;
-
-    // NOTE: Enum flags with '&' result in a boolean value
-    if (binop->type->kind == Type_Kind_Enum && binop->type->Enum.is_flags && binop->operation == Binary_Op_And) {
-         binop->type = &basic_types[Basic_Kind_Bool];
-    }
-
-    if (all_checks_are_final) {
-        binop->flags |= Ast_Flag_Has_Been_Checked;
-
-        if (binop->flags & Ast_Flag_Comptime) {
-            // NOTE: Not a binary op
-            *pbinop = (AstBinaryOp *) ast_reduce(context.ast_alloc, (AstTyped *) binop);
-        }
-    }
-
-    return Check_Success;
-
-bad_binaryop:
-    report_bad_binaryop(binop);
-
-    return Check_Error;
-}
-
-CheckStatus check_unaryop(AstUnaryOp** punop) {
-    AstUnaryOp* unaryop = *punop;
-
-    CHECK(expression, &unaryop->expr);
-
-    if (unaryop->operation != Unary_Op_Negate) {
-        resolve_expression_type(unaryop->expr);
-    }
-
-    if (unaryop->operation == Unary_Op_Cast) {
-        char* err;
-        if (unaryop->type == NULL)
-            YIELD(unaryop->token->pos, "Trying to resolve destination type for cast.");
-
-        if (!cast_is_legal(unaryop->expr->type, unaryop->type, &err)) {
-            ERROR_(unaryop->token->pos, "Cast Error: %s", err);
-        }
-
-    } else {
-        unaryop->type = unaryop->expr->type;
-    }
-
-    if (unaryop->operation == Unary_Op_Not) {
-        if (!type_is_bool(unaryop->expr->type)) {
-            ERROR_(unaryop->token->pos,
-                    "Bool negation operator expected bool type, got '%s'.",
-                    node_get_type_name(unaryop->expr));
-        }
-    }
-
-    if (unaryop->operation == Unary_Op_Bitwise_Not) {
-        if (!type_is_integer(unaryop->expr->type)) {
-            ERROR_(unaryop->token->pos,
-                    "Bitwise operator expected integer type, got '%s'.",
-                    node_get_type_name(unaryop->expr));
-        }
-    }
-
-    if (unaryop->expr->flags & Ast_Flag_Comptime) {
-        unaryop->flags |= Ast_Flag_Comptime;
-        // NOTE: Not a unary op
-        *punop = (AstUnaryOp *) ast_reduce(context.ast_alloc, (AstTyped *) unaryop);
-    }
-
-    return Check_Success;
-}
-
-CheckStatus check_struct_literal(AstStructLiteral* sl) {
-
-    if (sl->type == NULL) {
-        // NOTE: This is used for automatically typed struct literals. If there is no provided
-        // type for the struct literal, assume that it is passes successfully. When it is used
-        // elsewhere, it will be added as an expression entity that will be processed once the
-        // stnode is filled out.
-        if (sl->stnode == NULL) {
-            CHECK(arguments, &sl->args);
-
-            return Check_Success;
-        }
-
-        CHECK(expression, &sl->stnode);
-        if (!node_is_type((AstNode *) sl->stnode)) {
-            ERROR(sl->token->pos, "Type used for struct literal is not a type.");
-        }
-
-        sl->type = type_build_from_ast(context.ast_alloc, (AstType *) sl->stnode);
-        if (sl->type == NULL)
-            YIELD(sl->token->pos, "Trying to resolve type of struct literal.");
-    }
-
-    if (!type_is_structlike_strict(sl->type)) {
-        //
-        // If there are no given arguments to a structure literal, it is treated as a 'zero-value',
-        // and can be used to create a completely zeroed value of any type.
-        if (bh_arr_length(sl->args.values) == 0 && bh_arr_length(sl->args.named_values) == 0) {
-            AstZeroValue *zv = make_zero_value(context.ast_alloc, sl->token, sl->type);
-            bh_arr_push(sl->args.values, (AstTyped *) zv);
-
-            sl->flags |= Ast_Flag_Has_Been_Checked;
-            return Check_Success;
-        }
-
-        if ((sl->flags & Ast_Flag_Has_Been_Checked) != 0) {
-            assert(sl->args.values);
-            assert(sl->args.values[0]);
-            assert(sl->args.values[0]->kind == Ast_Kind_Zero_Value);
-            return Check_Success;
-        }
-
-        //
-        // Otherwise, it is not possible to construct the type if it is not a structure.
-        ERROR_(sl->token->pos,
-                "'%s' is not constructable using a struct literal.",
-                type_get_name(sl->type));
-    }
-
-    i32 mem_count = type_structlike_mem_count(sl->type);
-    arguments_ensure_length(&sl->args, mem_count);
-
-    // :Idempotency
-    if ((sl->flags & Ast_Flag_Has_Been_Checked) == 0) {
-        char* err_msg = NULL;
-        if (!fill_in_arguments(&sl->args, (AstNode *) sl, &err_msg, 1)) {
-            onyx_report_error(sl->token->pos, Error_Critical, err_msg);
-
-            bh_arr_each(AstTyped *, value, sl->args.values) {
-                if (*value == NULL) {
-                    i32 member_idx = value - sl->args.values; // Pointer subtraction hack
-                    StructMember smem;
-                    type_lookup_member_by_idx(sl->type, member_idx, &smem);
-
-                    onyx_report_error(sl->token->pos, Error_Critical,
-                        "Value not given for %d%s member, '%s', for type '%s'.",
-                        member_idx + 1, bh_num_suffix(member_idx + 1),
-                        smem.name, type_get_name(sl->type));
-                }
-            }
-
-            return Check_Error;
-        }
-    }
-    sl->flags |= Ast_Flag_Has_Been_Checked;
-
-    AstTyped** actual = sl->args.values;
-    StructMember smem;
-
-    // BUG: There are problems setting the comptime flag this late in the checking because
-    // if the struct literal was type inferred, then the literal won't be correctly determined
-    // to be comptime on the first pass, which is needed for top level expressions.
-    sl->flags |= Ast_Flag_Comptime;
-
-    fori (i, 0, mem_count) {
-        // NOTE: Not checking the return on this function because
-        // this for loop is bounded by the number of members in the
-        // type.
-        type_lookup_member_by_idx(sl->type, i, &smem);
-        Type* formal = smem.type;
-
-        CHECK(expression, actual);
-        if ((*actual)->type == NULL && (*actual)->entity != NULL && (*actual)->entity->state <= Entity_State_Check_Types) {
-            YIELD((*actual)->token->pos, "Trying to resolve type of expression for member.");
-        }
-
-        TYPE_CHECK(actual, formal) {
-            ERROR_(sl->token->pos,
-                    "Mismatched types for %d%s member named '%s', expected '%s', got '%s'.",
-                    i + 1, bh_num_suffix(i + 1),
-                    smem.name,
-                    type_get_name(formal),
-                    node_get_type_name(*actual));
-        }
-
-        sl->flags &= ((*actual)->flags & Ast_Flag_Comptime) | (sl->flags &~ Ast_Flag_Comptime);
-        actual++;
-    }
-
-    return Check_Success;
-}
-
-CheckStatus check_array_literal(AstArrayLiteral* al) {
-    // :Idempotency
-    if ((al->flags & Ast_Flag_Array_Literal_Typed) == 0) {
-        if (al->atnode == NULL) return Check_Success;
-            // YIELD(al->token->pos, "Waiting for array literal type to be known.");
-
-        CHECK(expression, &al->atnode);
-        if (!node_is_type((AstNode *) al->atnode))
-            ERROR(al->token->pos, "Array type is not a type.");
-
-        al->type = type_build_from_ast(context.ast_alloc, (AstType *) al->atnode);
-        if (al->type == NULL)
-            YIELD(al->token->pos, "Trying to resolve type of array literal.");
-
-        al->type = type_make_array(context.ast_alloc, al->type, bh_arr_length(al->values));
-        if (al->type == NULL || al->type->kind != Type_Kind_Array)
-            ERROR(al->token->pos, "Expected array type for array literal. This is a compiler bug.");
-
-        al->flags |= Ast_Flag_Array_Literal_Typed;
-    }
-
-    if (al->type->Array.count != (u32) bh_arr_length(al->values)) {
-        ERROR_(al->token->pos, "Wrong array size (%d) for number of values (%d).",
-            al->type->Array.count, bh_arr_length(al->values));
-    }
-
-    al->flags |= Ast_Flag_Comptime;
-    assert(al->type->kind == Type_Kind_Array);
-
-    Type* elem_type = al->type->Array.elem;
-    bh_arr_each(AstTyped *, expr, al->values) {
-        CHECK(expression, expr);
-
-        // HACK HACK HACK
-        if ((*expr)->type == NULL &&
-            (*expr)->entity != NULL &&
-            (*expr)->entity->state <= Entity_State_Check_Types) {
-            YIELD_(al->token->pos, "Trying to resolve type of %d%s element of array literal.", expr - al->values, bh_num_suffix(expr - al->values));
-        }
-
-        al->flags &= ((*expr)->flags & Ast_Flag_Comptime) | (al->flags &~ Ast_Flag_Comptime);
-
-        TYPE_CHECK(expr, elem_type) {
-            ERROR_((*expr)->token->pos, "Mismatched types for value of in array, expected '%s', got '%s'.",
-                type_get_name(elem_type),
-                node_get_type_name(*expr));
-        }
-    }
-
-    return Check_Success;
-}
-
-CheckStatus check_range_literal(AstRangeLiteral** prange) {
-    AstRangeLiteral* range = *prange;
-    if (range->flags & Ast_Flag_Has_Been_Checked) return Check_Success;
-
-    CHECK(expression, &range->low);
-    CHECK(expression, &range->high);
-
-    builtin_range_type_type = type_build_from_ast(context.ast_alloc, builtin_range_type);
-    if (builtin_range_type_type == NULL) YIELD(range->token->pos, "Waiting for 'range' structure to be built.");
-
-    Type* expected_range_type = builtin_range_type_type;
-    StructMember smem;
-
-    type_lookup_member(expected_range_type, "low", &smem);
-    TYPE_CHECK(&range->low, smem.type) {
-        ERROR_(range->token->pos,
-            "Expected left side of range to be a 32-bit integer, got '%s'.",
-            node_get_type_name(range->low));
-    }
-
-    type_lookup_member(expected_range_type, "high", &smem);
-    TYPE_CHECK(&range->high, smem.type) {
-        ERROR_(range->token->pos,
-            "Expected right side of range to be a 32-bit integer, got '%s'.",
-            node_get_type_name(range->high));
-    }
-
-    if (range->step == NULL) {
-        type_lookup_member(expected_range_type, "step", &smem);
-        assert(smem.initial_value != NULL);
-        CHECK(expression, smem.initial_value);
-
-        range->step = *smem.initial_value;
-    }
-
-    range->flags |= Ast_Flag_Has_Been_Checked;
-    return Check_Success;
-}
-
-CheckStatus check_compound(AstCompound* compound) {
-    bh_arr_each(AstTyped *, expr, compound->exprs) {
-        CHECK(expression, expr);
-    }
-
-    compound->type = type_build_compound_type(context.ast_alloc, compound);
-    return Check_Success;
-}
-
-CheckStatus check_if_expression(AstIfExpression* if_expr) {
-    CHECK(expression, &if_expr->cond);
-    CHECK(expression, &if_expr->true_expr);
-    CHECK(expression, &if_expr->false_expr);
-
-    TYPE_CHECK(&if_expr->cond, &basic_types[Basic_Kind_Bool]) {
-        ERROR_(if_expr->token->pos, "If-expression expected boolean for condition, got '%s'.",
-            type_get_name(if_expr->cond->type));
-    }
-
-    resolve_expression_type((AstTyped *) if_expr);
-
-    if (!types_are_compatible(if_expr->true_expr->type, if_expr->false_expr->type)) {
-        ERROR_(if_expr->token->pos, "Mismatched types for if-expression, left side is '%s', and right side is '%s'.",
-            type_get_name(if_expr->true_expr->type), type_get_name(if_expr->false_expr->type));
-    }
-
-    return Check_Success;
-}
-
-CheckStatus check_do_block(AstDoBlock** pdoblock) {
-    AstDoBlock* doblock = *pdoblock;
-    if (doblock->flags & Ast_Flag_Has_Been_Checked) return Check_Success;
-
-    fill_in_type((AstTyped *) doblock);
-
-    Type** old_expected_return_type = expected_return_type;
-    expected_return_type = &doblock->type;
-
-    doblock->block->rules = Block_Rule_Do_Block;
-    CHECK(block, doblock->block);
-
-    if (doblock->type == &type_auto_return) doblock->type = &basic_types[Basic_Kind_Void];
-
-    expected_return_type = old_expected_return_type;
-    doblock->flags |= Ast_Flag_Has_Been_Checked;
-    return Check_Success;
-}
-
-CheckStatus check_address_of(AstAddressOf** paof) {
-    AstAddressOf* aof = *paof;
-
-    AstTyped* expr = (AstTyped *) strip_aliases((AstNode *) aof->expr);
-    if (expr->kind == Ast_Kind_Subscript && bh_arr_length(operator_overloads[Binary_Op_Ptr_Subscript]) > 0) {
-        if (aof->potential_substitute == NULL) {
-            CHECK(expression, &((AstSubscript *) expr)->addr);
-            CHECK(expression, &((AstSubscript *) expr)->expr);
-
-            AstBinaryOp *op = onyx_ast_node_new(context.ast_alloc, sizeof(AstBinaryOp), Ast_Kind_Binary_Op);
-            op->operation = Binary_Op_Ptr_Subscript;
-            op->left  = ((AstSubscript *) expr)->addr;
-            op->right = ((AstSubscript *) expr)->expr;
-            op->token = aof->token;
-
-            aof->potential_substitute = op;
-        }
-
-        AstCall* call = binaryop_try_operator_overload(aof->potential_substitute, NULL);
-        if (call == (AstCall *) &node_that_signals_a_yield) YIELD(aof->token->pos, "Waiting for operator overload to possibly resolve.");
-        if (call != NULL) {
-            call->next = aof->next;
-            *(AstCall **) paof = call;
-
-            CHECK(call, (AstCall **) paof);
-            return Check_Success;
-        }
-    }
-
-    CHECK(expression, &aof->expr);
-    if (node_is_addressable_literal((AstNode *) aof->expr)) {
-        resolve_expression_type(aof->expr);
-    }
-    
-    if (aof->expr->type == NULL) {
-        YIELD(aof->token->pos, "Trying to resolve type of expression to take a reference.");
-    }
-
-    expr = (AstTyped *) strip_aliases((AstNode *) aof->expr);
-    if (node_is_type((AstNode *) expr)) {
-        AstPointerType *pt = onyx_ast_node_new(context.ast_alloc, sizeof(AstPointerType), Ast_Kind_Pointer_Type);
-        pt->token     = aof->token;
-        pt->elem      = (AstType *) expr;
-        pt->__unused  = aof->next;
-        *paof         = (AstAddressOf *) pt;
-        CHECK(type, (AstType **) &pt);
-        return Check_Success;
-    }
-
-    if ((expr->kind != Ast_Kind_Subscript
-            && expr->kind != Ast_Kind_Dereference
-            && expr->kind != Ast_Kind_Field_Access
-            && expr->kind != Ast_Kind_Memres
-            && expr->kind != Ast_Kind_Local
-            && expr->kind != Ast_Kind_Constraint_Sentinel
-            && !node_is_addressable_literal((AstNode *) expr))
-            || (expr->flags & Ast_Flag_Cannot_Take_Addr) != 0) {
-
-        if (aof->can_be_removed) {
-            *(AstTyped **) paof = aof->expr;
-            return Check_Yield_Macro;
-        }
-
-        ERROR_(aof->token->pos, "Cannot take the address of something that is not an l-value. %s", onyx_ast_node_kind_string(expr->kind));
-    }
-
-    expr->flags |= Ast_Flag_Address_Taken;
-
-    aof->type = type_make_pointer(context.ast_alloc, expr->type);
-
-    return Check_Success;
-}
-
-CheckStatus check_dereference(AstDereference* deref) {
-    CHECK(expression, &deref->expr);
-
-    if (!type_is_pointer(deref->expr->type))
-        ERROR(deref->token->pos, "Cannot dereference non-pointer value.");
-
-    if (deref->expr->type == basic_type_rawptr.basic_type)
-        ERROR(deref->token->pos, "Cannot dereference 'rawptr'. Cast to another pointer type first.");
-
-    deref->type = deref->expr->type->Pointer.elem;
-
-    return Check_Success;
-}
-
-CheckStatus check_subscript(AstSubscript** psub) {
-    AstSubscript* sub = *psub;
-    CHECK(expression, &sub->addr);
-    CHECK(expression, &sub->expr);
-
-    if (sub->addr->type == NULL) YIELD(sub->token->pos, "Waiting to know type of left-hand side of subscript.");
-
-    // NOTE: Try operator overloading before checking everything else.
-    if (sub->expr->type != NULL &&
-        (sub->addr->type->kind != Type_Kind_Basic || sub->expr->type->kind != Type_Kind_Basic)) {
-        // AstSubscript is the same as AstBinaryOp for the first sizeof(AstBinaryOp) bytes
-        AstBinaryOp* binop = (AstBinaryOp *) sub;
-        AstCall *implicit_call = binaryop_try_operator_overload(binop, NULL);
-
-        if (implicit_call == (AstCall *) &node_that_signals_a_yield)
-            YIELD(sub->token->pos, "Trying to resolve operator overload.");
-
-        if (implicit_call != NULL) {
-            // NOTE: Not an array access
-            implicit_call->next = sub->next;
-            *psub = (AstSubscript *) implicit_call;
-
-            CHECK(call, (AstCall **) psub);
-            return Check_Success;
-        }
-    }
-
-    if (!type_is_array_accessible(sub->addr->type)) {
-        report_bad_binaryop((AstBinaryOp *) sub);
-        return Check_Error;
-    }
-
-    if (sub->addr->type->kind == Type_Kind_Slice || sub->addr->type->kind == Type_Kind_DynArray || sub->addr->type->kind == Type_Kind_VarArgs) {
-        // If we are accessing on a slice or a dynamic array, implicitly add a field access for the data member
-        StructMember smem;
-        type_lookup_member(sub->addr->type, "data", &smem);
-
-        AstFieldAccess* fa = make_field_access(context.ast_alloc, sub->addr, "data");
-        fa->type   = smem.type;
-        fa->offset = smem.offset;
-        fa->idx    = smem.idx;
-
-        sub->addr = (AstTyped *) fa;
-    }
-
-    if (types_are_compatible(sub->expr->type, builtin_range_type_type)) {
-        Type *of = type_get_contained_type(sub->addr->type);
-        if (of == NULL) {
-            // FIXME: Slice creation should be allowed for slice types and dynamic array types, like it
-            // is below, but this code doesn't look at that.
-            report_bad_binaryop((AstBinaryOp *) sub);
-            ERROR(sub->token->pos, "Invalid type for left of slice creation.");
-        }
-
-        sub->kind = Ast_Kind_Slice;
-        sub->type = type_make_slice(context.ast_alloc, of);
-        sub->elem_size = type_size_of(of);
-
-        return Check_Success;
-    }
-
-    resolve_expression_type(sub->expr);
-    if (!type_is_small_integer(sub->expr->type)) {
-        report_bad_binaryop((AstBinaryOp *) sub);
-        ERROR_(sub->token->pos, "Expected small integer type for index, got '%s'.", node_get_type_name(sub->expr));
-    }
-
-    sub->type = type_get_contained_type(sub->addr->type);
-    if (sub->type == NULL) {
-        report_bad_binaryop((AstBinaryOp *) sub);
-        ERROR(sub->token->pos, "Invalid type for left of array access.");
-    }
-
-    sub->elem_size = type_size_of(sub->type);
-    return Check_Success;
-}
-
-CheckStatus check_field_access(AstFieldAccess** pfield) {
-    AstFieldAccess* field = *pfield;
-    if (field->flags & Ast_Flag_Has_Been_Checked) return Check_Success;
-
-    CHECK(expression, &field->expr);
-    if (field->expr->type == NULL) {
-        YIELD(field->token->pos, "Trying to resolve type of source expression.");
-    }
-
-    if (!type_is_structlike(field->expr->type)) {
-        ERROR_(field->token->pos,
-            "Cannot access field '%b' on '%s'. Type is not a struct.",
-            field->token->text,
-            field->token->length,
-            node_get_type_name(field->expr));
-    }
-
-    // Optimization for (*foo).member.
-    if (field->expr->kind == Ast_Kind_Dereference) {
-        field->expr = ((AstDereference *) field->expr)->expr;
-    }
-
-    if (field->token != NULL && field->field == NULL) {
-        token_toggle_end(field->token);
-        field->field = bh_strdup(context.ast_alloc, field->token->text);
-        token_toggle_end(field->token);
-    }
-
-    if (field->expr->type->kind == Type_Kind_Struct) {
-        if (field->expr->type->Struct.status != SPS_Uses_Done) {
-            YIELD(field->token->pos, "Waiting for struct type to be completed before looking up members.");
-        }
-    }
-
-    StructMember smem;
-    if (!type_lookup_member(field->expr->type, field->field, &smem)) {
-        if (field->expr->type->kind == Type_Kind_Array) {
-            if (!strcmp(field->field, "count")) {
-                *pfield = (AstFieldAccess *) make_int_literal(context.ast_alloc, field->expr->type->Array.count);
-                return Check_Success;
-            }
-        }
-
-        AstNode* n = try_symbol_raw_resolve_from_type(field->expr->type, field->field);
-
-        AstType* type_node = field->expr->type->ast_type;
-        if (!n) n = try_symbol_raw_resolve_from_node((AstNode *) type_node, field->field);
-
-        if (n) {
-            *pfield = (AstFieldAccess *) n;
-            return Check_Success;
-        }
-
-        char* closest = find_closest_symbol_in_node((AstNode *) type_node, field->field);
-        if (closest) {
-            ERROR_(field->token->pos, "Field '%s' does not exists on '%s'. Did you mean '%s'?", field->field, node_get_type_name(field->expr), closest);
-        } else {
-            ERROR_(field->token->pos, "Field '%s' does not exists on '%s'.", field->field, node_get_type_name(field->expr));
-        }
-    }
-
-    // NOTE: If this member was included into the structure through a "use x: ^T" kind of statement,
-    // then we have to insert a intermediate field access in order to access the correct member.
-    if (smem.use_through_pointer_index >= 0) {
-        StructMember containing_member;
-        assert(type_lookup_member_by_idx(field->expr->type, smem.use_through_pointer_index, &containing_member));
-
-        AstFieldAccess *new_access = onyx_ast_node_new(context.ast_alloc, sizeof(AstFieldAccess), Ast_Kind_Field_Access);
-        new_access->token = field->token;
-        new_access->offset = containing_member.offset;
-        new_access->idx = containing_member.idx;
-        new_access->type = containing_member.type;
-        new_access->expr = field->expr;
-        new_access->flags |= Ast_Flag_Has_Been_Checked;
-
-        field->expr = (AstTyped *) new_access;
-    }
-
-    field->offset = smem.offset;
-    field->idx = smem.idx;
-    field->type = smem.type;
-    field->flags |= Ast_Flag_Has_Been_Checked;
-    return Check_Success;
-}
-
-CheckStatus check_method_call(AstBinaryOp** pmcall) {
-    AstBinaryOp* mcall = *pmcall;
-    CHECK(expression, &mcall->left);
-    if (mcall->left->type == NULL) YIELD(mcall->token->pos, "Trying to resolve type of left hand side.");
-
-    mcall->type = mcall->left->type;
-    AstTyped* implicit_argument = mcall->left;
-
-    // Symbol resolution should have ensured that this is call node.
-    AstCall* call_node = (AstCall *) mcall->right;
-    assert(call_node->kind == Ast_Kind_Call);
-
-    // :Idempotency
-    if ((mcall->flags & Ast_Flag_Has_Been_Checked) == 0) {
-        // Implicitly take the address of the value if it is not already a pointer type.
-        // This could be weird to think about semantically so some testing with real code
-        // would be good.                                      - brendanfh 2020/02/05
-        if (implicit_argument->type->kind != Type_Kind_Pointer) {
-            AstAddressOf *address_of = make_address_of(context.ast_alloc, implicit_argument);
-            address_of->can_be_removed = 1;
-            implicit_argument = (AstTyped *) address_of;
-        }
-
-        implicit_argument = (AstTyped *) make_argument(context.ast_alloc, implicit_argument);
-
-        bh_arr_insertn(call_node->args.values, 0, 1);
-        call_node->args.values[0] = implicit_argument;
-
-        *pmcall = (AstBinaryOp *) mcall->right;
-        mcall->right->next = mcall->next;
-    }
-    mcall->flags |= Ast_Flag_Has_Been_Checked;
-
-    CHECK(call, (AstCall **) pmcall);
-    return Check_Success;
-}
-
-CheckStatus check_size_of(AstSizeOf* so) {
-    CHECK(type, &so->so_ast_type);
-
-    so->so_type = type_build_from_ast(context.ast_alloc, so->so_ast_type);
-    if (so->so_type == NULL)
-        YIELD(so->token->pos, "Trying to resolve type to take the size of.");
-
-    so->size = type_size_of(so->so_type);
-    so->flags |= Ast_Flag_Comptime;
-
-    return Check_Success;
-}
-
-CheckStatus check_align_of(AstAlignOf* ao) {
-    CHECK(type, &ao->ao_ast_type);
-
-    ao->ao_type = type_build_from_ast(context.ast_alloc, ao->ao_ast_type);
-    if (ao->ao_type == NULL)
-        YIELD(ao->token->pos, "Trying to resolve type to take the alignment of.");
-
-    ao->alignment = type_alignment_of(ao->ao_type);
-    ao->flags |= Ast_Flag_Comptime;
-
-    return Check_Success;
-}
-
-CheckStatus check_expression(AstTyped** pexpr) {
-    AstTyped* expr = *pexpr;
-    if (expr->kind > Ast_Kind_Type_Start && expr->kind < Ast_Kind_Type_End) {
-        // This is to ensure that the type will exist when compiling. For example, a poly-call type
-        // would have to wait for the entity to pass through, which the code generation does not know
-        // about.
-        CHECK(type, (AstType **) pexpr);
-        expr = *pexpr;
-
-        // Don't try to construct a polystruct ahead of time because you can't.
-        if (expr->kind != Ast_Kind_Poly_Struct_Type) {
-            if (type_build_from_ast(context.ast_alloc, (AstType*) expr) == NULL) {
-                YIELD(expr->token->pos, "Trying to construct type.");
-            }
-        } else {
-            type_build_from_ast(context.ast_alloc, (AstType*) expr);
-        }
-
-        expr->type = &basic_types[Basic_Kind_Type_Index];
-        return Check_Success;
-    }
-
-    if (expr->kind == Ast_Kind_Polymorphic_Proc) {
-        // polymorphic procedures do not need to be checked. Their concrete instantiations
-        // will be checked when they are created.
-        return Check_Success;
-    }
-
-    if (expr->kind == Ast_Kind_Macro) {
-        return Check_Success;
-    }
-
-    if (expr->kind == Ast_Kind_Directive_Init) {
-        ERROR(expr->token->pos, "#init declarations are not in normal expressions, only in #after clauses.");
-    }
-
-    fill_in_type(expr);
-    current_checking_level = EXPRESSION_LEVEL;
-
-    CheckStatus retval = Check_Success;
-    switch (expr->kind) {
-        case Ast_Kind_Binary_Op: retval = check_binaryop((AstBinaryOp **) pexpr); break;
-        case Ast_Kind_Unary_Op:  retval = check_unaryop((AstUnaryOp **) pexpr); break;
-
-        case Ast_Kind_Intrinsic_Call:
-        case Ast_Kind_Call:     retval = check_call((AstCall **) pexpr); break;
-        case Ast_Kind_Argument: retval = check_argument((AstArgument **) pexpr); break;
-        case Ast_Kind_Block:    retval = check_block((AstBlock *) expr); break;
-
-        case Ast_Kind_Symbol:
-            YIELD_(expr->token->pos, "Waiting to resolve symbol, '%b'.", expr->token->text, expr->token->length);
-            break;
-
-        case Ast_Kind_Param:
-            if (expr->type == NULL) {
-                YIELD(expr->token->pos, "Waiting on parameter type.");
-            }
-            break;
-
-        case Ast_Kind_Local: break;
-
-        case Ast_Kind_Address_Of:    retval = check_address_of((AstAddressOf **) pexpr); break;
-        case Ast_Kind_Dereference:   retval = check_dereference((AstDereference *) expr); break;
-        case Ast_Kind_Slice:
-        case Ast_Kind_Subscript:     retval = check_subscript((AstSubscript **) pexpr); break;
-        case Ast_Kind_Field_Access:  retval = check_field_access((AstFieldAccess **) pexpr); break;
-        case Ast_Kind_Method_Call:   retval = check_method_call((AstBinaryOp **) pexpr); break;
-        case Ast_Kind_Size_Of:       retval = check_size_of((AstSizeOf *) expr); break;
-        case Ast_Kind_Align_Of:      retval = check_align_of((AstAlignOf *) expr); break;
-        case Ast_Kind_Range_Literal: retval = check_range_literal((AstRangeLiteral **) pexpr); break;
-
-        case Ast_Kind_Global:
-            if (expr->type == NULL) {
-                onyx_report_error(expr->token->pos, Error_Critical, "Global with unknown type.");
-                retval = Check_Error;
-            }
-            break;
-
-        case Ast_Kind_NumLit:
-            assert(expr->type != NULL);
-            break;
-
-        case Ast_Kind_Struct_Literal:
-            retval = check_struct_literal((AstStructLiteral *) expr);
-            break;
-
-        case Ast_Kind_Array_Literal:
-            retval = check_array_literal((AstArrayLiteral *) expr);
-            break;
-
-        case Ast_Kind_Function:
-            // NOTE: Will need something like this at some point
-            // AstFunction* func = (AstFunction *) expr;
-            // bh_arr_each(AstParam, param, func->params) {
-            //     if (param->default_value != NULL) {
-            //         onyx_message_add(Msg_Type_Literal,
-            //                 func->token->pos,
-            //                 "cannot use functions with default parameters in this way");
-            //         retval = 1;
-            //         break;
-            //     }
-            // }
-            if (expr->type == NULL)
-                YIELD(expr->token->pos, "Waiting for function type to be resolved.");
-
-            expr->flags |= Ast_Flag_Function_Used;
-            break;
-
-        case Ast_Kind_Directive_Solidify:
-            CHECK(directive_solidify, (AstDirectiveSolidify **) pexpr);
-            break;
-
-        case Ast_Kind_Directive_Defined:
-            *pexpr = (AstTyped *) make_bool_literal(context.ast_alloc, ((AstDirectiveDefined *) expr)->is_defined);
-            fill_in_type(*pexpr);
-            break;
-
-        case Ast_Kind_Compound:
-            CHECK(compound, (AstCompound *) expr);
-            break;
-
-        case Ast_Kind_Call_Site:
-            // NOTE: This has to be set here because if it were to be set in the parser,
-            // builtin_callsite_type wouldn't be known when parsing the builtin.onyx file.
-            expr->type_node = builtin_callsite_type;
-            break;
-
-        case Ast_Kind_If_Expression:
-            CHECK(if_expression, (AstIfExpression *) expr);
-            break;
-
-        case Ast_Kind_Alias:
-            CHECK(expression, &((AstAlias *) expr)->alias);
-            expr->flags |= (((AstAlias *) expr)->alias->flags & Ast_Flag_Comptime);
-            expr->type = ((AstAlias *) expr)->alias->type;
-            break;
-
-        case Ast_Kind_Directive_Insert:
-            retval = check_insert_directive((AstDirectiveInsert **) pexpr);
-            break;
-
-        case Ast_Kind_Code_Block:
-            expr->flags |= Ast_Flag_Comptime;
-            fill_in_type(expr);
-            break;
-
-        case Ast_Kind_Do_Block:
-            retval = check_do_block((AstDoBlock **) pexpr);
-            break;
-
-        case Ast_Kind_Memres:
-            if (expr->type == NULL) YIELD(expr->token->pos, "Waiting to know globals type.");
-            break;
-
-        case Ast_Kind_StrLit: break;
-        case Ast_Kind_File_Contents: break;
-        case Ast_Kind_Overloaded_Function: break;
-        case Ast_Kind_Enum_Value: break;
-        case Ast_Kind_Polymorphic_Proc: break;
-        case Ast_Kind_Package: break;
-        case Ast_Kind_Error: break;
-        case Ast_Kind_Unary_Field_Access: break;
-        case Ast_Kind_Constraint_Sentinel: break;
-        case Ast_Kind_Switch_Case: break;
-        case Ast_Kind_Foreign_Block: break;
-        case Ast_Kind_Zero_Value: break;
-
-        default:
-            retval = Check_Error;
-            onyx_report_error(expr->token->pos, Error_Critical, "UNEXPECTED INTERNAL COMPILER ERROR");
-            DEBUG_HERE;
-            break;
-    }
-
-    return retval;
-}
-
-CheckStatus check_global(AstGlobal* global) {
-    fill_in_type((AstTyped *) global);
-
-    if (global->type == NULL) {
-        YIELD(global->token->pos, "Trying to resolve type for global.");
-    }
-
-    return Check_Success;
-}
-
-CheckStatus check_insert_directive(AstDirectiveInsert** pinsert) {
-    AstDirectiveInsert* insert = *pinsert;
-    if (insert->flags & Ast_Flag_Has_Been_Checked) return Check_Success;
-
-    CHECK(expression, &insert->code_expr);
-    if (insert->code_expr->type == NULL) {
-        if (insert->code_expr->entity && insert->code_expr->entity->state >= Entity_State_Code_Gen) {
-            ERROR(insert->token->pos, "Expected expression of type 'Code'.");
-        }
-
-        // Bad wording for the message.
-        YIELD(insert->token->pos, "Waiting for resolution to code expression type.");
-    }
-
-    Type* code_type = type_build_from_ast(context.ast_alloc, builtin_code_type);
-
-    TYPE_CHECK(&insert->code_expr, code_type) {
-        ERROR_(insert->token->pos, "#unquote expected a value of type 'Code', got '%s'.",
-            type_get_name(insert->code_expr->type));
-    }
-
-    AstCodeBlock* code_block = (AstCodeBlock *) insert->code_expr;
-    code_block = (AstCodeBlock *) strip_aliases((AstNode *) code_block);
-
-    assert(code_block->kind == Ast_Kind_Code_Block);
-
-    AstNode* cloned_block = ast_clone(context.ast_alloc, code_block->code);
-    cloned_block->next = insert->next;
-    *(AstNode **) pinsert = cloned_block;
-
-    insert->flags |= Ast_Flag_Has_Been_Checked;
-
-    return Check_Return_To_Symres;
-}
-
-CheckStatus check_directive_solidify(AstDirectiveSolidify** psolid) {
-    AstDirectiveSolidify* solid = *psolid;
-
-    bh_arr_each(AstPolySolution, sln, solid->known_polyvars) {
-        CHECK(expression, &sln->value);
-
-        if (node_is_type((AstNode *) sln->value)) {
-            sln->type = type_build_from_ast(context.ast_alloc, sln->ast_type);
-            sln->kind = PSK_Type;
-        } else {
-            sln->kind = PSK_Value;
-        }
-    }
-
-    solid->resolved_proc = polymorphic_proc_try_solidify(solid->poly_proc, solid->known_polyvars, solid->token);
-    if (solid->resolved_proc == (AstNode *) &node_that_signals_a_yield) {
-        solid->resolved_proc = NULL;
-        YIELD(solid->token->pos, "Waiting for partially solidified procedure.");
-    }
-
-    // NOTE: Not a DirectiveSolidify.
-    *psolid = (AstDirectiveSolidify *) solid->resolved_proc;
-
-    return Check_Success;
-}
-
-CheckStatus check_remove_directive(AstDirectiveRemove *remove) {
-    if (!inside_for_iterator) {
-        ERROR(remove->token->pos, "#remove is only allowed in the body of a for-loop over an iterator.");
-    }
-
-    return Check_Success;
-}
-
-CheckStatus check_statement(AstNode** pstmt) {
-    AstNode* stmt = *pstmt;
-
-    current_checking_level = STATEMENT_LEVEL;
-
-    switch (stmt->kind) {
-        case Ast_Kind_Jump:       return Check_Success;
-
-        case Ast_Kind_Return:     return check_return((AstReturn *) stmt);
-        case Ast_Kind_If:         return check_if((AstIfWhile *) stmt);
-        case Ast_Kind_Static_If:  return check_if((AstIfWhile *) stmt);
-        case Ast_Kind_While:      return check_while((AstIfWhile *) stmt);
-        case Ast_Kind_For:        return check_for((AstFor *) stmt);
-        case Ast_Kind_Switch:     return check_switch((AstSwitch *) stmt);
-        case Ast_Kind_Block:      return check_block((AstBlock *) stmt);
-        case Ast_Kind_Defer:      return check_statement(&((AstDefer *) stmt)->stmt);
-        case Ast_Kind_Directive_Remove: return check_remove_directive((AstDirectiveRemove *) stmt);
-        case Ast_Kind_Call: {
-            CHECK(call, (AstCall **) pstmt);
-            (*pstmt)->flags |= Ast_Flag_Expr_Ignored;
-            return Check_Success;
-        }
-
-        case Ast_Kind_Binary_Op:
-            CHECK(binaryop, (AstBinaryOp **) pstmt);
-            (*pstmt)->flags |= Ast_Flag_Expr_Ignored;
-            return Check_Success;
-
-        // NOTE: Local variable declarations used to be removed after the symbol
-        // resolution phase because long long ago, all locals needed to be known
-        // in a block in order to efficiently allocate enough space and registers
-        // for them all. Now with LocalAllocator, this is no longer necessary.
-        // Therefore, locals stay in the tree and need to be passed along.
-        case Ast_Kind_Local: {
-            AstTyped* typed_stmt = (AstTyped *) stmt;
-            fill_in_type(typed_stmt);
-            if (typed_stmt->type_node != NULL && typed_stmt->type == NULL) {
-                CHECK(type, &typed_stmt->type_node);
-
-                if (!node_is_type((AstNode *) typed_stmt->type_node)) {
-                    ERROR(stmt->token->pos, "Local's type is not a type.");
-                }
-
-                YIELD(typed_stmt->token->pos, "Waiting for local variable's type.");
-            }
-
-            if (typed_stmt->next != NULL && typed_stmt->next->kind == Ast_Kind_Binary_Op) {
-                AstBinaryOp *next = (AstBinaryOp *) typed_stmt->next;
-                if (next->operation == Binary_Op_Assign && next->left == typed_stmt) {
-                    typed_stmt->flags |= Ast_Flag_Decl_Followed_By_Init;
-                }
-            }
-            return Check_Success;
-        }
-
-        default:
-            CHECK(expression, (AstTyped **) pstmt);
-            (*pstmt)->flags |= Ast_Flag_Expr_Ignored;
-            return Check_Success;
-    }
-}
-
-CheckStatus check_statement_chain(AstNode** start) {
-    while (*start) {
-        CHECK(statement, start);
-        start = &(*start)->next;
-    }
-
-    return Check_Success;
-}
-
-CheckStatus check_block(AstBlock* block) {
-    // This used to use statement_chain, but since block optimize which statements need to be rechecked,
-    // it has to be its own thing.
-
-    AstNode** start = &block->body;
-    fori (i, 0, block->statement_idx) {
-        start = &(*start)->next;
-    }
-
-    while (*start) {
-        CheckStatus cs = check_statement(start);
-        switch (cs) {
-            case Check_Success:
-                start = &(*start)->next;
-                block->statement_idx++;
-                break;
-
-            case Check_Return_To_Symres:
-                block->statement_idx = 0;
-
-            default:
-                return cs;
-        }
-
-    }
-
-    return Check_Success;
-}
-
-CheckStatus check_function(AstFunction* func) {
-    if (func->flags & Ast_Flag_Has_Been_Checked) return Check_Success;
-    if (func->entity_header && func->entity_header->state < Entity_State_Code_Gen)
-        YIELD(func->token->pos, "Waiting for procedure header to pass type-checking");
-
-    bh_arr_each(AstTyped *, pexpr, func->tags) {
-        CHECK(expression, pexpr);
-
-        if (((*pexpr)->flags & Ast_Flag_Comptime) == 0) {
-            ERROR((*pexpr)->token->pos, "#tag expressions should be compile time known.");
-        }
-    }
-
-    inside_for_iterator = 0;
-    expected_return_type = &func->type->Function.return_type;
-    if (func->body) {
-        CheckStatus status = check_block(func->body);
-        if (status == Check_Error && func->generated_from && context.cycle_detected == 0)
-            ERROR(func->generated_from->pos, "Error in polymorphic procedure generated from this location.");
-
-        if (status != Check_Success) {
-            expected_return_type = NULL;
-            return status;
-        }
-    }
-
-    if (*expected_return_type == &type_auto_return) {
-        *expected_return_type = &basic_types[Basic_Kind_Void];
-    }
-
-    func->flags |= Ast_Flag_Has_Been_Checked;
-    return Check_Success;
-}
-
-CheckStatus check_overloaded_function(AstOverloadedFunction* func) {
-    b32 done = 1;
-
-    bh_imap all_overloads;
-    bh_imap_init(&all_overloads, global_heap_allocator, 4);
-    build_all_overload_options(func->overloads, &all_overloads);
-
-    bh_arr_each(bh__imap_entry, entry, all_overloads.entries) {
-        AstTyped* node = (AstTyped *) entry->key;
-        if (node->kind == Ast_Kind_Overloaded_Function) continue;
-
-        if (   node->kind != Ast_Kind_Function
-            && node->kind != Ast_Kind_Polymorphic_Proc
-            && node->kind != Ast_Kind_Macro) {
-            onyx_report_error(node->token->pos, Error_Critical, "Overload option not procedure or macro. Got '%s'",
-                onyx_ast_node_kind_string(node->kind));
-
-            bh_imap_free(&all_overloads);
-            return Check_Error;
-        }
-
-        if (node->kind == Ast_Kind_Function) {
-            AstFunction* func = (AstFunction *) node;
-
-            if (func->entity_header && func->entity_header->state <= Entity_State_Check_Types) {
-                done = 0;
-            }
-        }
-    }
-
-    bh_imap_free(&all_overloads);
-
-    if (done) return Check_Success;
-    else      YIELD(func->token->pos, "Waiting for all options to pass type-checking.");
-}
-
-CheckStatus check_struct(AstStructType* s_node) {
-    if (s_node->entity_defaults && s_node->entity_defaults->state < Entity_State_Check_Types)
-        YIELD(s_node->token->pos, "Waiting for struct member defaults to pass symbol resolution.");
-
-    if (s_node->min_size_)      CHECK(expression, &s_node->min_size_);
-    if (s_node->min_alignment_) CHECK(expression, &s_node->min_alignment_);
-
-    if (s_node->polymorphic_argument_types) {
-        assert(s_node->polymorphic_arguments);
-
-        fori (i, 0, (i64) bh_arr_length(s_node->polymorphic_argument_types)) {
-            Type *arg_type = type_build_from_ast(context.ast_alloc, s_node->polymorphic_argument_types[i]);
-            if (arg_type == NULL) YIELD(s_node->polymorphic_argument_types[i]->token->pos, "Waiting to build type for polymorph argument.");
-
-            // CLEANUP: This might be wrong...
-            if (s_node->polymorphic_arguments[i].value) {
-                TYPE_CHECK(&s_node->polymorphic_arguments[i].value, arg_type) {
-                    ERROR_(s_node->polymorphic_arguments[i].value->token->pos, "Expected value of type %s, got %s.",
-                        type_get_name(arg_type),
-                        type_get_name(s_node->polymorphic_arguments[i].value->type));
-                }
-            }
-        }
-    }
-
-    if (s_node->constraints.constraints) {
-        s_node->constraints.produce_errors = (s_node->flags & Ast_Flag_Header_Check_No_Error) == 0;
-
-        OnyxFilePos pos = s_node->token->pos;
-        if (s_node->polymorphic_error_loc.filename) {
-            pos = s_node->polymorphic_error_loc;
-        }
-        CHECK(constraint_context, &s_node->constraints, s_node->scope, pos);
-    }
-
-    //
-    // This ensures that all procedures defined inside of a structure are
-    // not pruned and omitted from the binary. This is because a large
-    // use-case of procedures in structures is dynamically linking them
-    // using the type info data.
-    if (s_node->scope) {
-        fori (i, 0, shlen(s_node->scope->symbols)) {
-            AstNode* node = s_node->scope->symbols[i].value;
-            if (node->kind == Ast_Kind_Function) {
-                node->flags |= Ast_Flag_Function_Used;
-            }
-        }
-    }
-
-    bh_arr_each(AstStructMember *, smem, s_node->members) {
-        if ((*smem)->type_node != NULL) {
-            CHECK(type, &(*smem)->type_node);
-        }
-
-        if ((*smem)->type_node == NULL && (*smem)->initial_value != NULL) {
-            CHECK(expression, &(*smem)->initial_value);
-
-            fill_in_type((*smem)->initial_value);
-            if ((*smem)->initial_value->type == NULL)
-                YIELD((*smem)->initial_value->token->pos, "Trying to resolve type for initial value for member.");
-
-            resolve_expression_type((*smem)->initial_value);
-            if ((*smem)->type == NULL) (*smem)->type = (*smem)->initial_value->type;
-
-            if ((*smem)->type == NULL) {
-                ERROR((*smem)->initial_value->token->pos, "Unable to deduce type of initial value. This is probably a compiler bug.");
-            }
-        }
-    }
-
-    // NOTE: fills in the pending_type.
-    s_node->ready_to_build_type = 1;
-    type_build_from_ast(context.ast_alloc, (AstType *) s_node);
-    if (s_node->pending_type == NULL || !s_node->pending_type_is_valid)
-        YIELD(s_node->token->pos, "Waiting for type to be constructed.");
-
-    bh_arr_each(StructMember *, smem, s_node->pending_type->Struct.memarr) {
-        if ((*smem)->type->kind == Type_Kind_Compound) {
-            ERROR(s_node->token->pos, "Compound types are not allowed as struct member types.");
-        }
-
-        if ((*smem)->used) {
-            if (!type_struct_member_apply_use(context.ast_alloc, s_node->pending_type, *smem)) {
-                YIELD((*smem)->token->pos, "Waiting for use to be applied.");
-            }
-        }
-    }
-
-    s_node->stcache = s_node->pending_type;
-    s_node->stcache->Struct.status = SPS_Uses_Done;
-
-    return Check_Success;
-}
-
-CheckStatus check_struct_defaults(AstStructType* s_node) {
-    if (s_node->entity_type && s_node->entity_type->state < Entity_State_Code_Gen)
-        YIELD(s_node->token->pos, "Waiting for struct type to be constructed before checking defaulted members.");
-    if (s_node->entity_type && s_node->entity_type->state == Entity_State_Failed)
-        return Check_Failed;
-
-    if (s_node->meta_tags) {
-        bh_arr_each(AstTyped *, meta, s_node->meta_tags) {
-            CHECK(expression, meta);
-            resolve_expression_type(*meta);
-
-            if (((*meta)->flags & Ast_Flag_Comptime) == 0) {
-                onyx_report_error((*meta)->token->pos, Error_Critical, "#tag expressions are expected to be compile-time known.");
-                return Check_Error;
-            }
-        }
-    }
-
-    bh_arr_each(StructMember *, smem, s_node->stcache->Struct.memarr) {
-        if ((*smem)->initial_value && *(*smem)->initial_value) {
-            CHECK(expression, (*smem)->initial_value);
-
-            TYPE_CHECK((*smem)->initial_value, (*smem)->type) {
-                ERROR_((*(*smem)->initial_value)->token->pos,
-                        "Mismatched type for initial value, expected '%s', got '%s'.",
-                        type_get_name((*smem)->type),
-                        type_get_name((*(*smem)->initial_value)->type));
-            }
-
-            resolve_expression_type(*(*smem)->initial_value);
-        }
-
-        if ((*smem)->meta_tags) {
-            bh_arr_each(AstTyped *, meta, (*smem)->meta_tags) {
-                CHECK(expression, meta);
-                resolve_expression_type(*meta);
-
-                if (((*meta)->flags & Ast_Flag_Comptime) == 0) {
-                    onyx_report_error((*meta)->token->pos, Error_Critical, "#tag expressions are expected to be compile-time known.");
-                    return Check_Error;
-                }
-            }
-        }
-    }
-
-    return Check_Success;
-}
-
-CheckStatus check_temp_function_header(AstFunction* func) {
-    CheckStatus cs = check_function_header(func);
-    if (cs == Check_Error) {
-        if (func->flags & Ast_Flag_Header_Check_No_Error) {
-            onyx_clear_errors();
-        }
-
-        return Check_Failed;
-    }
-
-    if (cs != Check_Success) return cs;
-
-    return Check_Complete;
-}
-
-CheckStatus check_function_header(AstFunction* func) {
-    //if (func->entity_body && func->entity_body->state < Entity_State_Check_Types)
-    //    YIELD(func->token->pos, "Waiting for function body to complete symbol resolution to check header.");
-
-    b32 expect_default_param = 0;
-    b32 has_had_varargs = 0;
-
-    if (func->constraints.constraints != NULL && func->constraints.constraints_met == 0) {
-        func->constraints.produce_errors = (func->flags & Ast_Flag_Header_Check_No_Error) == 0;
-        CHECK(constraint_context, &func->constraints, func->scope, func->token->pos);
-
-        // All constraints have been met. Return to symbol resolution to finish
-        // looking up all symbols in the function.
-        return Check_Return_To_Symres;
-    }
-
-    bh_arr_each(AstParam, param, func->params) {
-        AstLocal* local = param->local;
-
-        if (expect_default_param && param->default_value == NULL) {
-            ERROR(local->token->pos,
-                    "All parameters must have default values after the first default valued parameter.");
-        }
-
-        if (has_had_varargs && param->vararg_kind != VA_Kind_Not_VA) {
-            ERROR(local->token->pos,
-                    "Can only have one param that is of variable argument type.");
-        }
-
-        if (has_had_varargs && param->vararg_kind != VA_Kind_Not_VA) {
-            ERROR(local->token->pos,
-                    "Variable arguments must be last in parameter list");
-        }
-
-        if (param->vararg_kind == VA_Kind_Untyped) {
-            // HACK
-            if (builtin_vararg_type_type == NULL)
-                builtin_vararg_type_type = type_build_from_ast(context.ast_alloc, builtin_vararg_type);
-
-            local->type = builtin_vararg_type_type;
-        }
-
-        if (param->default_value != NULL) {
-            if (param->vararg_kind != VA_Kind_Not_VA) {
-                ERROR(local->token->pos, "Variadic arguments cannot have default values.");
-            }
-
-            CHECK(expression, &param->default_value);
-
-            if (local->type_node == NULL && local->type == NULL) {
-                local->type = resolve_expression_type(param->default_value);
-            }
-
-            expect_default_param = 1;
-        }
-
-        if (local->type_node != NULL) {
-            // If the function has the no_error flag, then the type node should have it set too.
-            // This allows for polymorphic structures with constraints to fail gracefully.
-            local->type_node->flags |= (func->flags & Ast_Flag_Header_Check_No_Error);
-            CHECK(type, &local->type_node);
-        }
-
-        fill_in_type((AstTyped *) local);
-        if (local->type == NULL) {
-            YIELD(local->token->pos, "Waiting for parameter type to be known.");
-        }
-
-        if (local->type == (Type *) &node_that_signals_failure) {
-            return Check_Failed;
-        }
-
-        if (local->type->kind == Type_Kind_Compound) {
-            ERROR(param->local->token->pos, "Compound types are not allowed as parameter types. Try splitting this into multiple parameters.");
-        }
-
-        // NOTE: I decided to make parameter default values not type checked against
-        // the actual parameter type. The actual type checking will happen in check_call
-        // when the default value is used as an argument and then has to be checked against
-        // the parameter type                                  - brendanfh 2021/01/06
-        // if (param->default_value != NULL) {
-        //     if (!unify_node_and_type(&param->default_value, param->local->type)) {
-        //         onyx_report_error(param->local->token->pos,
-        //                 "Expected default value of type '%s', was of type '%s'.",
-        //                 type_get_name(param->local->type),
-        //                 type_get_name(param->default_value->type));
-        //         return Check_Error;
-        //     }
-        // }
-
-        if (param->vararg_kind != VA_Kind_Not_VA) has_had_varargs = 1;
-
-        if (local->type->kind != Type_Kind_Array && type_size_of(local->type) == 0) {
-            ERROR(local->token->pos, "Function parameters cannot have zero-width types.");
-        }
-    }
-
-    if (func->return_type != NULL) CHECK(type, &func->return_type);
-
-    func->type = type_build_function_type(context.ast_alloc, func);
-    if (func->type == NULL) YIELD(func->token->pos, "Waiting for function type to be constructed");
-
-    return Check_Success;
-}
-
-CheckStatus check_memres_type(AstMemRes* memres) {
-    CHECK(type, &memres->type_node);
-    fill_in_type((AstTyped *) memres);
-    if (memres->type_node && !memres->type) YIELD(memres->token->pos, "Waiting for global type to be constructed.");
-    return Check_Success;
-}
-
-CheckStatus check_memres(AstMemRes* memres) {
-    assert(memres->type_entity);
-    if (memres->type_entity->state < Entity_State_Code_Gen) YIELD(memres->token->pos, "Waiting for global to pass type construction.");
-
-    if (memres->initial_value != NULL) {
-        if (memres->threadlocal) {
-            onyx_report_error(memres->token->pos, Error_Critical, "'#thread_local' variables cannot have an initializer at the moment.");
-            return Check_Error;
-        }
-
-        CHECK(expression, &memres->initial_value);
-
-        if (memres->type != NULL) {
-            Type* memres_type = memres->type;
-            TYPE_CHECK(&memres->initial_value, memres_type) {
-                ERROR_(memres->token->pos,
-                        "Cannot assign value of type '%s' to a '%s'.",
-                        node_get_type_name(memres->initial_value),
-                        type_get_name(memres_type));
-            }
-
-        } else {
-            resolve_expression_type(memres->initial_value);
-            if (memres->initial_value->type == NULL && memres->initial_value->entity != NULL && memres->initial_value->entity->state <= Entity_State_Check_Types) {
-                YIELD(memres->token->pos, "Waiting for global type to be constructed.");
-            }
-            memres->type = memres->initial_value->type;
-        }
-
-        if ((memres->initial_value->flags & Ast_Flag_Comptime) == 0) {
-            if (memres->initial_value->entity != NULL && memres->initial_value->entity->state <= Entity_State_Check_Types) {
-                YIELD(memres->token->pos, "Waiting for initial value to be checked.");
-            }
-
-            ERROR(memres->initial_value->token->pos, "Top level expressions must be compile time known.");
-        }
-    }
-
-    return Check_Success;
-}
-
-CheckStatus check_type(AstType** ptype) {
-    if (ptype == NULL || *ptype == NULL) return Check_Success;
-
-    AstType* type = *ptype;
-    AstType* original_type = type;
-    while (type->kind == Ast_Kind_Type_Alias)
-        type = ((AstTypeAlias *) type)->to;
-
-    if (type->flags & Ast_Flag_Has_Been_Checked) return Check_Success;
-
-    switch (type->kind) {
-        case Ast_Kind_Poly_Call_Type: {
-            AstPolyCallType* pc_node = (AstPolyCallType *) type;
-
-            bh_arr_each(AstNode *, param, pc_node->params) {
-                if (!node_is_type(*param)) {
-                    CHECK(expression, (AstTyped **) param);
-                    resolve_expression_type((AstTyped *) *param);
-                    fill_in_type((AstTyped *) *param);
-                }
-            }
-
-            break;
-        }
-
-        case Ast_Kind_Typeof: {
-            AstTypeOf *type_of = (AstTypeOf *) type;
-            CHECK(expression, (AstTyped **) &type_of->expr);
-            resolve_expression_type(type_of->expr);
-
-            if (type_of->expr->type == NULL) {
-                YIELD(type_of->token->pos, "Trying to check type for type-of expression.");
-            }
-
-            type_of->resolved_type = type_of->expr->type;
-            break;
-        }
-
-        case Ast_Kind_Pointer_Type: ((AstPointerType *) type)->elem->flags |= type->flags & Ast_Flag_Header_Check_No_Error; CHECK(type, &((AstPointerType *) type)->elem); break;
-        case Ast_Kind_Slice_Type:   ((AstSliceType *) type)->elem->flags |= type->flags & Ast_Flag_Header_Check_No_Error; CHECK(type, &((AstSliceType *) type)->elem); break;
-        case Ast_Kind_DynArr_Type:  ((AstDynArrType *) type)->elem->flags |= type->flags & Ast_Flag_Header_Check_No_Error; CHECK(type, &((AstDynArrType *) type)->elem); break;
-        case Ast_Kind_VarArg_Type:  ((AstVarArgType *) type)->elem->flags |= type->flags & Ast_Flag_Header_Check_No_Error; CHECK(type, &((AstVarArgType *) type)->elem); break;
-
-        case Ast_Kind_Function_Type: {
-            AstFunctionType* ftype = (AstFunctionType *) type;
-
-            CHECK(type, &ftype->return_type);
-
-            if (ftype->param_count > 0) {
-                fori (i, 0, (i64) ftype->param_count) {
-                    CHECK(type, &ftype->params[i]);
-                }
-            }
-            break;
-        }
-
-        case Ast_Kind_Type_Compound: {
-            AstCompoundType* ctype = (AstCompoundType *) type;
-
-            bh_arr_each(AstType *, type, ctype->types) CHECK(type, type);
-            break;
-        }
-
-        case Ast_Kind_Array_Type: {
-            AstArrayType* atype = (AstArrayType *) type;
-            if (atype->count_expr) {
-                CHECK(expression, &atype->count_expr);
-                resolve_expression_type(atype->count_expr);
-            }
-
-            break;
-        }
-
-        case Ast_Kind_Field_Access: {
-            CHECK(field_access, (AstFieldAccess **) ptype);
-            type = *ptype;
-            original_type = type;
-
-            if (!node_is_type((AstNode *) type)) {
-                ERROR_(original_type->token->pos, "This field access did not resolve to be a type. It resolved to be a '%s'.", onyx_ast_node_kind_string(type->kind));
-            }
-            break;
-        }
-    }
-
-    type = original_type;
-    type->flags |= Ast_Flag_Comptime;
-    while (type->kind == Ast_Kind_Type_Alias) {
-        type->flags |= Ast_Flag_Comptime;
-        type = ((AstTypeAlias *) type)->to;
-    }
-
-    type->flags |= Ast_Flag_Has_Been_Checked;
-    return Check_Success;
-}
-
-CheckStatus check_static_if(AstIf* static_if) {
-    expression_types_must_be_known = 1;
-    CheckStatus result = check_expression(&static_if->cond);
-    expression_types_must_be_known = 0;
-    if (result == Check_Yield_Macro) return Check_Yield_Macro;
-
-    if (result > Check_Errors_Start || !(static_if->cond->flags & Ast_Flag_Comptime)) {
-        ERROR(static_if->token->pos, "Expected this condition to be compile time known.");
-    }
-
-    if (!type_is_bool(static_if->cond->type)) {
-        ERROR(static_if->token->pos, "Expected this condition to be a boolean value.");
-    }
-
-    static_if->flags |= Ast_Flag_Static_If_Resolved;
-
-    b32 resolution = static_if_resolution(static_if);
-
-    if (context.options->print_static_if_results)
-        bh_printf("Static if statement at %s:%d:%d resulted in %s\n",
-            static_if->token->pos.filename,
-            static_if->token->pos.line,
-            static_if->token->pos.column,
-            resolution ? "true" : "false");
-
-    if (resolution) {
-        bh_arr_each(Entity *, ent, static_if->true_entities) {
-            entity_heap_insert_existing(&context.entities, *ent);
-        }
-
-    } else {
-        bh_arr_each(Entity *, ent, static_if->false_entities) {
-            entity_heap_insert_existing(&context.entities, *ent);
-        }
-    }
-
-    return Check_Complete;
-}
-
-CheckStatus check_process_directive(AstNode* directive) {
-    if (directive->kind == Ast_Kind_Directive_Export) {
-        AstDirectiveExport *export = (AstDirectiveExport *) directive;
-        AstTyped *exported = export->export;
-        if (exported->entity && exported->entity->state <= Entity_State_Check_Types)
-            YIELD(directive->token->pos, "Waiting for exported type to be known.");
-
-        CHECK(expression, &export->export_name_expr);
-
-        if (export->export_name_expr->kind != Ast_Kind_StrLit) {
-            ERROR_(export->token->pos, "Expected export name to be a string literal, got '%s'.", onyx_ast_node_kind_string(export->export_name_expr->kind));
-        }
-
-        export->export_name = export->export_name_expr->token;
-    }
-
-    if (directive->kind == Ast_Kind_Directive_Init) {
-        AstDirectiveInit *init = (AstDirectiveInit *) directive;
-        if ((init->flags & Ast_Flag_Has_Been_Checked) == 0) {
-            CHECK(expression, &init->init_proc);
-
-            if (init->init_proc->kind != Ast_Kind_Function) {
-                ERROR_(init->token->pos, "#init only works for functions, got '%s'", onyx_ast_node_kind_string(init->init_proc->kind));
-            }
-
-            assert(init->init_proc->type);
-            if (init->init_proc->type->Function.param_count != 0) {
-                ERROR(init->token->pos, "#init expects a function that takes 0 arguments.");
-            }
-        }
-
-        init->flags |= Ast_Flag_Has_Been_Checked;
-
-        if (init->dependencies) {
-            i32 i = 0;
-            bh_arr_each(AstDirectiveInit *, dependency, init->dependencies) {
-                AstTyped *d = (AstTyped *) strip_aliases((AstNode *) *dependency);
-                if (d->kind != Ast_Kind_Directive_Init) {
-                    ERROR_(init->token->pos, "All dependencies of an #init must be another #init. The %d%s dependency was not.", i + 1, bh_num_suffix(i + 1));
-                }
-
-                assert(d->entity);
-                if (d->entity->state != Entity_State_Finalized) {
-                    YIELD(init->token->pos, "Circular dependency in #init nodes. Here are the nodes involved.");
-                }
-
-                i++;
-            }
-        }
-
-        bh_arr_push(init_procedures, (AstFunction *) init->init_proc);
-        return Check_Complete;
-    }
-
-    if (directive->kind == Ast_Kind_Directive_Library) {
-        AstDirectiveLibrary *library = (AstDirectiveLibrary *) directive;
-
-        if (library->library_symbol->kind != Ast_Kind_StrLit) {
-            ERROR_(library->token->pos, "#library directive expected compile-time known string for library name. Got '%s'.",
-                onyx_ast_node_kind_string(library->library_symbol->kind));
-        }
-
-        AstStrLit *symbol = (AstStrLit *) library->library_symbol;
-        char* temp_name     = bh_alloc_array(global_scratch_allocator, char, symbol->token->length);
-        i32   temp_name_len = string_process_escape_seqs(temp_name, symbol->token->text, symbol->token->length);
-        library->library_name = bh_strdup(global_heap_allocator, temp_name);
-        return Check_Success;
-    }
-
-    return Check_Success;
-}
-
-CheckStatus check_macro(AstMacro* macro) {
-    if (macro->body->kind == Ast_Kind_Function) {
-        CHECK(function_header, (AstFunction *) macro->body);
-    }
-
-    return Check_Success;
-}
-
-CheckStatus check_constraint(AstConstraint *constraint) {
-    switch (constraint->phase) {
-        case Constraint_Phase_Cloning_Expressions: {
-            if (constraint->interface->kind == Ast_Kind_Symbol) {
-                return Check_Return_To_Symres;
-            }
-
-            if (constraint->interface->kind != Ast_Kind_Interface) {
-                // CLEANUP: This error message might not look totally right in some cases.
-                ERROR_(constraint->token->pos, "'%b' is not an interface. It is a '%s'.",
-                    constraint->token->text, constraint->token->length,
-                    onyx_ast_node_kind_string(constraint->interface->kind));
-            }
-
-            bh_arr_new(global_heap_allocator, constraint->exprs, bh_arr_length(constraint->interface->exprs));
-            bh_arr_each(InterfaceConstraint, ic, constraint->interface->exprs) {
-                InterfaceConstraint new_ic = {0};
-                new_ic.expr = (AstTyped *) ast_clone(context.ast_alloc, (AstNode *) ic->expr);
-                new_ic.expected_type_expr = (AstType *) ast_clone(context.ast_alloc, (AstNode *) ic->expected_type_expr);
-                new_ic.invert_condition = ic->invert_condition;
-                bh_arr_push(constraint->exprs, new_ic);
-            }
-
-            assert(constraint->interface->entity && constraint->interface->entity->scope);
-
-            constraint->scope = scope_create(context.ast_alloc, constraint->interface->entity->scope, constraint->token->pos);
-
-            fori (i, 0, bh_arr_length(constraint->interface->params)) {
-                InterfaceParam *ip = &constraint->interface->params[i];
-
-                AstTyped *sentinel = onyx_ast_node_new(context.ast_alloc, sizeof(AstTyped), Ast_Kind_Constraint_Sentinel);
-                sentinel->token = ip->value_token;
-                sentinel->type_node = constraint->type_args[i];
-
-                AstAlias *type_alias = onyx_ast_node_new(context.ast_alloc, sizeof(AstAlias), Ast_Kind_Alias);
-                type_alias->token = ip->type_token;
-                type_alias->alias = (AstTyped *) constraint->type_args[i];
-
-                symbol_introduce(constraint->scope, ip->value_token, (AstNode *) sentinel);
-                symbol_introduce(constraint->scope, ip->type_token, (AstNode *) type_alias);
-            }
-
-            assert(constraint->entity);
-            constraint->entity->scope = constraint->scope;
-
-            constraint->phase = Constraint_Phase_Checking_Expressions;
-            return Check_Return_To_Symres;
-        }
-
-        case Constraint_Phase_Checking_Expressions: {
-            fori (i, constraint->expr_idx, bh_arr_length(constraint->exprs)) {
-                InterfaceConstraint* ic = &constraint->exprs[i];
-
-                CheckStatus cs = check_expression(&ic->expr);
-                if (cs == Check_Return_To_Symres || cs == Check_Yield_Macro) {
-                    return cs;
-                }
-
-                if (cs == Check_Error && !ic->invert_condition) {
-                    goto constraint_error;
-                }
-
-                if (cs == Check_Success && ic->invert_condition) {
-                    goto constraint_error;
-                }
-
-                if (ic->expected_type_expr) {
-                    cs = check_type(&ic->expected_type_expr);
-                    if (cs == Check_Return_To_Symres || cs == Check_Yield_Macro) {
-                        return cs;
-                    }
-
-                    ic->expected_type = type_build_from_ast(context.ast_alloc, ic->expected_type_expr);
-                    if (ic->expected_type == NULL) {
-                        YIELD(ic->expected_type_expr->token->pos, "Waiting on expected type expression to be resolved.");
-                    }
-
-                    TYPE_CHECK(&ic->expr, ic->expected_type) {
-                        if (!ic->invert_condition)
-                            goto constraint_error;
-                    }
-                }
-
-                constraint->expr_idx++;
-                continue;
-
-              constraint_error:
-                // HACK HACK HACK
-                onyx_clear_errors();
-                *constraint->report_status = Constraint_Check_Status_Failed;
-                return Check_Failed;
-            }
-
-            // HACK HACK HACK
-            onyx_clear_errors();
-            *constraint->report_status = Constraint_Check_Status_Success;
-            return Check_Complete;
-        }
-    }
-
-    return Check_Success;
-}
-
-CheckStatus check_constraint_context(ConstraintContext *cc, Scope *scope, OnyxFilePos pos) {
-    if (cc->constraint_checks) {
-        if (cc->constraints_met == 1) return Check_Success;
-
-        fori (i, 0, bh_arr_length(cc->constraints)) {
-            if (cc->constraint_checks[i] == Constraint_Check_Status_Failed) {
-                if (cc->produce_errors) {
-                    AstConstraint *constraint = cc->constraints[i];
-                    char constraint_map[512] = {0};
-                    fori (i, 0, bh_arr_length(constraint->type_args)) {
-                        if (i != 0) strncat(constraint_map, ", ", 511);
-
-                        OnyxToken* symbol = constraint->interface->params[i].value_token;
-                        token_toggle_end(symbol);
-                        strncat(constraint_map, symbol->text, 511);
-                        token_toggle_end(symbol);
-
-                        strncat(constraint_map, " is of type '", 511);
-                        strncat(constraint_map, type_get_name(type_build_from_ast(context.ast_alloc, constraint->type_args[i])), 511);
-                        strncat(constraint_map, "'", 511);
-                    }
-
-                    onyx_report_error(constraint->exprs[constraint->expr_idx].expr->token->pos, Error_Critical, "Failed to satisfy constraint where %s.", constraint_map);
-                    onyx_report_error(constraint->token->pos, Error_Critical, "Here is where the interface was used.");
-                    onyx_report_error(pos, Error_Critical, "Here is the code that caused this constraint to be checked.");
-
-                    return Check_Error;
-
-                } else {
-                    // If no error are suppose to be produced, we still need to signal that
-                    // the node reached a completed state.
-                    return Check_Failed;
-                }
-            }
-
-            if (cc->constraint_checks[i] == Constraint_Check_Status_Queued) {
-                YIELD(pos, "Waiting for constraints to be checked.");
-            }
-        }
-
-        cc->constraints_met = 1;
-        return Check_Success;
-
-    } else {
-        u32 count = bh_arr_length(cc->constraints);
-        ConstraintCheckStatus *ccs = bh_alloc_array(context.ast_alloc, ConstraintCheckStatus, count);
-
-        cc->constraint_checks = ccs;
-
-        fori (i, 0, count) {
-            ccs[i] = Constraint_Check_Status_Queued;
-            cc->constraints[i]->report_status = &ccs[i];
-            cc->constraints[i]->phase = Constraint_Phase_Cloning_Expressions;
-
-            add_entities_for_node(NULL, (AstNode *) cc->constraints[i], scope, NULL);
-        }
-
-        return Check_Yield_Macro;
-    }
-}
-
-CheckStatus check_polyquery(AstPolyQuery *query) {
-    if (query->function_header->scope == NULL)
-        query->function_header->scope = scope_create(context.ast_alloc, query->proc->parent_scope_of_poly_proc, query->token->pos);
-
-    CheckStatus header_check = check_temp_function_header(query->function_header);
-    if (header_check == Check_Return_To_Symres) return Check_Return_To_Symres;
-
-    b32 solved_something = 0;
-    i32 solved_count = 0;
-    char *err_msg = NULL;
-    bh_arr_each(AstPolyParam, param, query->proc->poly_params) {
-        AstPolySolution sln;
-        bh_arr_each(AstPolySolution, solved_sln, query->slns) {
-            if (token_equals(param->poly_sym->token, solved_sln->poly_sym->token)) {
-                goto poly_query_done;
-            }
-        }
-
-        // CLEANUP: I think this can go away because it is already done in polymorph.c
-        bh_arr_each(AstPolySolution, known_sln, query->proc->known_slns) {
-            if (token_equals(param->poly_sym->token, known_sln->poly_sym->token)) {
-                sln = *known_sln;
-                goto poly_var_solved;
-            }
-        }
-
-        TypeMatch result = find_polymorphic_sln(&sln, param, query->function_header, query->pp_lookup, query->given, &err_msg);
-
-        switch (result) {
-            case TYPE_MATCH_SUCCESS:
-                goto poly_var_solved;
-
-            case TYPE_MATCH_SPECIAL:
-                return Check_Yield_Macro;
-
-            case TYPE_MATCH_YIELD:
-            case TYPE_MATCH_FAILED: {
-                if (query->successful_symres || solved_something) continue;
-
-                if (query->error_on_fail || context.cycle_detected) {
-                    onyx_report_error(query->token->pos, Error_Critical, "Error solving for polymorphic variable '%b'.", param->poly_sym->token->text, param->poly_sym->token->length);
-                    if (err_msg != NULL)  onyx_report_error(query->token->pos, Error_Critical, "%s", err_msg);
-                    if (query->error_loc) onyx_report_error(query->error_loc->pos, Error_Critical, "Here is where the call is located."); // :ErrorMessage
-                }
-
-                return Check_Failed;
-            }
-        }
-
-poly_var_solved:
-        solved_something = 1;
-        bh_arr_push(query->slns, sln);
-        insert_poly_sln_into_scope(query->function_header->scope, &sln);
-
-poly_query_done:
-        solved_count += 1;
-    }
-
-    if (solved_count != bh_arr_length(query->proc->poly_params)) {
-        if (solved_something || query->successful_symres) {
-            return Check_Return_To_Symres;
-        } else {
-            return Check_Failed;
-        }
-    }
-
-    return Check_Complete;
-}
-
-void check_entity(Entity* ent) {
-    CheckStatus cs = Check_Success;
-
-    switch (ent->type) {
-        case Entity_Type_Foreign_Function_Header:
-        case Entity_Type_Function_Header:          cs = check_function_header(ent->function); break;
-        case Entity_Type_Temp_Function_Header:     cs = check_temp_function_header(ent->function); break;
-        case Entity_Type_Function:                 cs = check_function(ent->function); break;
-        case Entity_Type_Overloaded_Function:      cs = check_overloaded_function(ent->overloaded_function); break;
-        case Entity_Type_Global:                   cs = check_global(ent->global); break;
-        case Entity_Type_Struct_Member_Default:    cs = check_struct_defaults((AstStructType *) ent->type_alias); break;
-        case Entity_Type_Memory_Reservation_Type:  cs = check_memres_type(ent->mem_res); break;
-        case Entity_Type_Memory_Reservation:       cs = check_memres(ent->mem_res); break;
-        case Entity_Type_Static_If:                cs = check_static_if(ent->static_if); break;
-        case Entity_Type_Macro:                    cs = check_macro(ent->macro); break;
-        case Entity_Type_Constraint_Check:         cs = check_constraint(ent->constraint); break;
-        case Entity_Type_Polymorph_Query:          cs = check_polyquery(ent->poly_query); break;
-        case Entity_Type_Enum_Value:               cs = check_expression(&ent->enum_value->value); break;
-        case Entity_Type_Process_Directive:        cs = check_process_directive((AstNode *) ent->expr); break;
-
-        case Entity_Type_Expression:
-            cs = check_expression(&ent->expr);
-            resolve_expression_type(ent->expr);
-            break;
-
-        case Entity_Type_Type_Alias:
-            if (ent->type_alias->kind == Ast_Kind_Struct_Type)
-                cs = check_struct((AstStructType *) ent->type_alias);
-            else
-                cs = check_type(&ent->type_alias);
-            break;
-
-        case Entity_Type_File_Contents:
-            if (context.options->no_file_contents) {
-                onyx_report_error(ent->expr->token->pos, Error_Critical, "#file_contents is disabled for this compilation.");
-            }
-            break;
-
-        default: break;
-    }
-
-    switch (cs) {
-        case Check_Yield_Macro:      ent->macro_attempts++; break;
-        case Check_Success:          ent->state = Entity_State_Code_Gen;        goto clear_attempts;
-        case Check_Complete:         ent->state = Entity_State_Finalized;       goto clear_attempts;
-        case Check_Return_To_Symres: ent->state = Entity_State_Resolve_Symbols; goto clear_attempts;
-        case Check_Failed:           ent->state = Entity_State_Failed;          goto clear_attempts;
-
-    clear_attempts:
-        ent->macro_attempts = 0;
-        ent->micro_attempts = 0;
-    }
-}
diff --git a/src/clone.c b/src/clone.c
deleted file mode 100644 (file)
index 828f661..0000000
+++ /dev/null
@@ -1,643 +0,0 @@
-#include "astnodes.h"
-#include "parser.h"
-#include "utils.h"
-
-// Weird flags that shouldn't be used too often because they complicate things
-static b32 dont_copy_structs = 0;
-
-static inline b32 should_clone(AstNode* node) {
-    if (node->flags & Ast_Flag_No_Clone) return 0;
-
-    if (dont_copy_structs) {
-        if (node->kind == Ast_Kind_Struct_Type) return 0;
-        if (node->kind == Ast_Kind_Function)    return 0;
-        if (node->kind == Ast_Kind_Polymorphic_Proc) return 0;
-    }
-
-    switch (node->kind) {
-        // List of nodes that should not be copied
-        case Ast_Kind_Global:
-        case Ast_Kind_Memres:
-        case Ast_Kind_StrLit:
-        case Ast_Kind_Package:
-        case Ast_Kind_Overloaded_Function:
-        case Ast_Kind_Alias:
-        case Ast_Kind_Code_Block:
-        case Ast_Kind_Macro:
-        case Ast_Kind_Symbol:
-        case Ast_Kind_Poly_Struct_Type:
-        case Ast_Kind_Basic_Type:
-        case Ast_Kind_Enum_Type:
-        case Ast_Kind_Enum_Value:
-            return 0;
-
-        default: return 1;
-    }
-}
-
-static inline i32 ast_kind_to_size(AstNode* node) {
-    switch (node->kind) {
-        case Ast_Kind_Error: return sizeof(AstNode);
-        case Ast_Kind_Package: return sizeof(AstPackage);
-        case Ast_Kind_Load_File: return sizeof(AstInclude);
-        case Ast_Kind_Load_Path: return sizeof(AstInclude);
-        case Ast_Kind_Load_All: return sizeof(AstInclude);
-        case Ast_Kind_Memres: return sizeof(AstMemRes);
-        case Ast_Kind_Binding: return sizeof(AstBinding);
-        case Ast_Kind_Function: return sizeof(AstFunction);
-        case Ast_Kind_Overloaded_Function: return sizeof(AstOverloadedFunction);
-        case Ast_Kind_Polymorphic_Proc: return sizeof(AstFunction);
-        case Ast_Kind_Block: return sizeof(AstBlock);
-        case Ast_Kind_Local: return sizeof(AstLocal);
-        case Ast_Kind_Global: return sizeof(AstGlobal);
-        case Ast_Kind_Symbol: return sizeof(AstNode);
-        case Ast_Kind_Unary_Op: return sizeof(AstUnaryOp);
-        case Ast_Kind_Binary_Op: return sizeof(AstBinaryOp);
-        case Ast_Kind_Type_Start: return 0;
-        case Ast_Kind_Type: return sizeof(AstType);
-        case Ast_Kind_Basic_Type: return sizeof(AstBasicType);
-        case Ast_Kind_Pointer_Type: return sizeof(AstPointerType);
-        case Ast_Kind_Function_Type: return sizeof(AstFunctionType) + ((AstFunctionType *) node)->param_count * sizeof(AstType *);
-        case Ast_Kind_Array_Type: return sizeof(AstArrayType);
-        case Ast_Kind_Slice_Type: return sizeof(AstSliceType);
-        case Ast_Kind_DynArr_Type: return sizeof(AstDynArrType);
-        case Ast_Kind_VarArg_Type: return sizeof(AstVarArgType);
-        case Ast_Kind_Struct_Type: return sizeof(AstStructType);
-        case Ast_Kind_Poly_Struct_Type: return sizeof(AstPolyStructType);
-        case Ast_Kind_Poly_Call_Type: return sizeof(AstPolyCallType);
-        case Ast_Kind_Enum_Type: return sizeof(AstEnumType);
-        case Ast_Kind_Type_Alias: return sizeof(AstTypeAlias);
-        case Ast_Kind_Type_Raw_Alias: return sizeof(AstTypeRawAlias);
-        case Ast_Kind_Type_Compound: return sizeof(AstCompoundType);
-        case Ast_Kind_Typeof: return sizeof(AstTypeOf);
-        case Ast_Kind_Type_End: return 0;
-        case Ast_Kind_Struct_Member: return sizeof(AstStructMember);
-        case Ast_Kind_Enum_Value: return sizeof(AstEnumValue);
-        case Ast_Kind_NumLit: return sizeof(AstNumLit);
-        case Ast_Kind_StrLit: return sizeof(AstStrLit);
-        case Ast_Kind_Param: return sizeof(AstLocal);
-        case Ast_Kind_Argument: return sizeof(AstArgument);
-        case Ast_Kind_Call: return sizeof(AstCall);
-        case Ast_Kind_Intrinsic_Call: return sizeof(AstCall);
-        case Ast_Kind_Return: return sizeof(AstReturn);
-        case Ast_Kind_Address_Of: return sizeof(AstAddressOf);
-        case Ast_Kind_Dereference: return sizeof(AstDereference);
-        case Ast_Kind_Subscript: return sizeof(AstSubscript);
-        case Ast_Kind_Slice: return sizeof(AstSubscript);
-        case Ast_Kind_Field_Access: return sizeof(AstFieldAccess);
-        case Ast_Kind_Unary_Field_Access: return sizeof(AstUnaryFieldAccess);
-        case Ast_Kind_Pipe: return sizeof(AstBinaryOp);
-        case Ast_Kind_Range_Literal: return sizeof(AstRangeLiteral);
-        case Ast_Kind_Method_Call: return sizeof(AstBinaryOp);
-        case Ast_Kind_Size_Of: return sizeof(AstSizeOf);
-        case Ast_Kind_Align_Of: return sizeof(AstAlignOf);
-        case Ast_Kind_File_Contents: return sizeof(AstFileContents);
-        case Ast_Kind_Struct_Literal: return sizeof(AstStructLiteral);
-        case Ast_Kind_Array_Literal: return sizeof(AstArrayLiteral);
-        case Ast_Kind_If: return sizeof(AstIfWhile);
-        case Ast_Kind_For: return sizeof(AstFor);
-        case Ast_Kind_While: return sizeof(AstIfWhile);
-        case Ast_Kind_Jump: return sizeof(AstJump);
-        case Ast_Kind_Use: return sizeof(AstUse);
-        case Ast_Kind_Defer: return sizeof(AstDefer);
-        case Ast_Kind_Switch: return sizeof(AstSwitch);
-        case Ast_Kind_Switch_Case: return sizeof(AstSwitchCase);
-        case Ast_Kind_Directive_Solidify: return sizeof(AstDirectiveSolidify);
-        case Ast_Kind_Compound: return sizeof(AstCompound);
-        case Ast_Kind_Named_Value: return sizeof(AstNamedValue);
-        case Ast_Kind_Call_Site: return sizeof(AstCallSite);
-        case Ast_Kind_Static_If: return sizeof(AstIfWhile);
-        case Ast_Kind_If_Expression: return sizeof(AstIfExpression);
-        case Ast_Kind_Directive_Insert: return sizeof(AstDirectiveInsert);
-        case Ast_Kind_Directive_Defined: return sizeof(AstDirectiveDefined);
-        case Ast_Kind_Do_Block: return sizeof(AstDoBlock);
-        case Ast_Kind_Constraint: return sizeof(AstConstraint);
-        case Ast_Kind_Directive_Remove: return sizeof(AstDirectiveRemove);
-        case Ast_Kind_Count: return 0;
-    }
-
-    return 0;
-}
-
-AstNode* ast_clone_list(bh_allocator a, void* n) {
-    AstNode* node = (AstNode *) n;
-    if (node == NULL) return NULL;
-
-    AstNode* root = ast_clone(a, node);
-    AstNode* curr = root->next;
-    AstNode** insertion = &root->next;
-
-    while (curr != NULL) {
-        curr = ast_clone(a, curr);
-        *insertion = curr;
-        insertion = &curr->next;
-        curr = curr->next;
-    }
-
-    return root;
-}
-
-static bh_arr(AstNode *) captured_entities=NULL;
-#define E(ent) do { \
-    assert(captured_entities); \
-    ent->entity = NULL; \
-    bh_arr_push(captured_entities, (AstNode *) ent); \
-    } while (0);
-    
-
-#define C(nt, mname) ((nt *) nn)->mname = (void *) ast_clone(a, ((nt *) node)->mname);
-
-// NOTE: Using void* to avoid a lot of unnecessary casting
-AstNode* ast_clone(bh_allocator a, void* n) {
-    AstNode* node = (AstNode *) n;
-
-    if (node == NULL) return NULL;
-    if (!should_clone(node)) return node;
-
-    static int clone_depth = 0;
-    clone_depth++;
-
-    i32 node_size = ast_kind_to_size(node);
-    // bh_printf("Cloning %s with size %d\n", onyx_ast_node_kind_string(node->kind), node_size);
-
-    AstNode* nn = onyx_ast_node_new(a, node_size, node->kind);
-    memmove(nn, node, node_size);
-
-    switch ((u16) node->kind) {
-        case Ast_Kind_Binary_Op:
-        case Ast_Kind_Pipe:
-        case Ast_Kind_Method_Call:
-            C(AstBinaryOp, left);
-            C(AstBinaryOp, right);
-            break;
-
-        case Ast_Kind_Unary_Op:
-            C(AstUnaryOp, expr);
-            C(AstUnaryOp, type_node);
-            break;
-
-        case Ast_Kind_Param:
-        case Ast_Kind_Local:
-            C(AstLocal, type_node);
-            break;
-
-        case Ast_Kind_Call:
-            C(AstCall, callee);
-            arguments_deep_clone(a, &((AstCall *) nn)->args, &((AstCall *) node)->args);
-            break;
-
-        case Ast_Kind_Argument:
-            C(AstArgument, value);
-            break;
-
-        case Ast_Kind_Address_Of:
-            C(AstAddressOf, expr);
-            break;
-
-        case Ast_Kind_Dereference:
-            C(AstDereference, expr);
-            break;
-
-        case Ast_Kind_Slice:
-        case Ast_Kind_Subscript:
-            C(AstSubscript, addr);
-            C(AstSubscript, expr);
-            break;
-
-        case Ast_Kind_Field_Access:
-            C(AstFieldAccess, expr);
-            break;
-
-        case Ast_Kind_Size_Of:
-            C(AstSizeOf, so_ast_type);
-            break;
-
-        case Ast_Kind_Align_Of:
-            C(AstAlignOf, ao_ast_type);
-            break;
-
-        case Ast_Kind_Struct_Literal: {
-            AstStructLiteral* st = (AstStructLiteral *) node;
-            AstStructLiteral* dt = (AstStructLiteral *) nn;
-
-            dt->stnode = (AstTyped *) ast_clone(a, st->stnode);
-
-            arguments_deep_clone(a, &dt->args, &st->args);
-            break;
-        }
-
-        case Ast_Kind_Array_Literal: {
-            AstArrayLiteral* st = (AstArrayLiteral *) node;
-            AstArrayLiteral* dt = (AstArrayLiteral *) nn;
-
-            dt->atnode = (AstTyped *) ast_clone(a, st->atnode);
-
-            dt->values = NULL;
-            bh_arr_new(global_heap_allocator, dt->values, bh_arr_length(st->values));
-            bh_arr_each(AstTyped *, val, st->values)
-                bh_arr_push(dt->values, (AstTyped *) ast_clone(a, *val));
-
-            break;
-        }
-
-        case Ast_Kind_Range_Literal:
-            C(AstRangeLiteral, low);
-            C(AstRangeLiteral, high);
-            C(AstRangeLiteral, step);
-            break;
-
-        case Ast_Kind_Return:
-            C(AstReturn, expr);
-            break;
-
-        case Ast_Kind_Block:
-            ((AstBlock *) nn)->body = ast_clone_list(a, ((AstBlock *) node)->body);
-            break;
-
-        case Ast_Kind_Defer:
-            C(AstDefer, stmt);
-            break;
-
-        case Ast_Kind_For:
-            C(AstFor, var);
-            C(AstFor, iter);
-            C(AstFor, stmt);
-            break;
-
-        case Ast_Kind_If:
-        case Ast_Kind_While:
-            ((AstIfWhile *) nn)->initialization = ast_clone_list(a, ((AstIfWhile *) node)->initialization);
-            //fallthrough
-
-        case Ast_Kind_Static_If:
-            C(AstIfWhile, cond);
-
-            C(AstIfWhile, true_stmt);
-            C(AstIfWhile, false_stmt);
-
-            if (nn->kind == Ast_Kind_Static_If) {
-                ((AstIfWhile *) node)->flags |= Ast_Flag_Dead;
-                ((AstIfWhile *) node)->flags |= Ast_Flag_Static_If_Resolved;
-
-                ((AstIfWhile *) nn)->flags &= ~Ast_Flag_Dead;
-                ((AstIfWhile *) nn)->flags &= ~Ast_Flag_Static_If_Resolved;
-                E(nn);
-            }
-
-            break;
-
-        case Ast_Kind_Switch_Case: {
-            C(AstSwitchCase, block);
-
-            AstSwitchCase *dw = (AstSwitchCase *) nn;
-            AstSwitchCase *sw = (AstSwitchCase *) node;
-
-            dw->values = NULL;
-            bh_arr_new(global_heap_allocator, dw->values, bh_arr_length(sw->values));
-            bh_arr_each(AstTyped *, value, sw->values)
-                bh_arr_push(dw->values, (AstTyped *) ast_clone(a, *value));
-
-            break;
-        }
-
-        case Ast_Kind_Switch: {
-            AstSwitch* dw = (AstSwitch *) nn;
-            AstSwitch* sw = (AstSwitch *) node;
-
-            dw->initialization = ast_clone_list(a, sw->initialization);
-            C(AstSwitch, expr);
-
-
-            dw->cases = NULL;
-            C(AstSwitch, case_block);
-            break;
-        }
-
-        case Ast_Kind_Pointer_Type:
-            C(AstPointerType, elem);
-            break;
-
-        case Ast_Kind_Array_Type:
-            C(AstArrayType, count_expr);
-            C(AstArrayType, elem);
-            break;
-
-        case Ast_Kind_Slice_Type:
-            C(AstSliceType, elem);
-            break;
-
-        case Ast_Kind_DynArr_Type:
-            C(AstDynArrType, elem);
-            break;
-
-        case Ast_Kind_VarArg_Type:
-            C(AstVarArgType, elem);
-            break;
-
-        case Ast_Kind_Type_Alias:
-            C(AstTypeAlias, to);
-            break;
-
-        case Ast_Kind_Struct_Type: {
-            AstStructType* ds = (AstStructType *) nn;
-            AstStructType* ss = (AstStructType *) node;
-
-            ds->members = NULL;
-            bh_arr_new(global_heap_allocator, ds->members, bh_arr_length(ss->members));
-
-            bh_arr_each(AstStructMember *, smem, ss->members) {
-                bh_arr_push(ds->members, (AstStructMember *) ast_clone(a, *smem));
-            }
-
-            ds->meta_tags = NULL;
-            bh_arr_new(global_heap_allocator, ds->meta_tags, bh_arr_length(ss->meta_tags));
-            bh_arr_each(AstTyped *, tag, ss->meta_tags) {
-                bh_arr_push(ds->meta_tags, (AstTyped *) ast_clone(a, *tag));
-            }
-            
-            if (ss->constraints.constraints) {
-                memset(&ds->constraints, 0, sizeof(ConstraintContext));
-                bh_arr_new(global_heap_allocator, ds->constraints.constraints, bh_arr_length(ss->constraints.constraints));
-
-                bh_arr_each(AstConstraint *, constraint, ss->constraints.constraints) {
-                    bh_arr_push(ds->constraints.constraints, (AstConstraint *) ast_clone(a, (AstNode *) *constraint));
-                }
-            }
-
-            ds->stcache = NULL;
-            break;
-        }
-
-        case Ast_Kind_Struct_Member: {
-            C(AstStructMember, type_node);
-            C(AstStructMember, initial_value);
-
-            AstStructMember *ds = (AstStructMember *) nn;
-            AstStructMember *ss = (AstStructMember *) node;
-
-            ds->meta_tags = NULL;
-            bh_arr_new(global_heap_allocator, ds->meta_tags, bh_arr_length(ss->meta_tags));
-            bh_arr_each(AstTyped *, tag, ss->meta_tags) {
-                bh_arr_push(ds->meta_tags, (AstTyped *) ast_clone(a, *tag));
-            }
-
-            break;
-        }
-
-        case Ast_Kind_Poly_Call_Type: {
-            AstPolyCallType* pcd = (AstPolyCallType *) nn;
-            AstPolyCallType* pcs = (AstPolyCallType *) node;
-
-            pcd->callee = (AstType *) ast_clone(a, pcs->callee);
-            pcd->params = NULL;
-            bh_arr_new(global_heap_allocator, pcd->params, bh_arr_length(pcs->params));
-
-            bh_arr_each(AstNode *, param, pcs->params) {
-                bh_arr_push(pcd->params, ast_clone(a, *param));
-            }
-
-            break;
-        }
-
-        case Ast_Kind_Type_Compound: {
-            AstCompoundType* cd = (AstCompoundType *) nn;
-            AstCompoundType* cs = (AstCompoundType *) node;
-
-            cd->types = NULL;
-            bh_arr_new(global_heap_allocator, cd->types, bh_arr_length(cs->types));
-
-            bh_arr_each(AstType *, type, cs->types) {
-                bh_arr_push(cd->types, (AstType *) ast_clone(a, (AstNode *) *type));
-            }
-            break;
-        }
-
-        case Ast_Kind_Function_Type:
-            C(AstFunctionType, return_type);
-            ((AstFunctionType *) nn)->param_count = ((AstFunctionType *) node)->param_count;
-            fori (i, 0, (i64) ((AstFunctionType *) nn)->param_count) {
-                ((AstFunctionType *) nn)->params[i] = (AstType *) ast_clone(a, ((AstFunctionType *) node)->params[i]);
-            }
-            break;
-
-        case Ast_Kind_Binding:
-            bh_printf("Cloning binding: %b\n", node->token->text, node->token->length);
-            C(AstTyped, type_node);
-            C(AstBinding, node);
-            break;
-
-        case Ast_Kind_Function:
-        case Ast_Kind_Polymorphic_Proc: {
-            if (clone_depth > 1) {
-                clone_depth--;
-                return node;
-            }
-
-            AstFunction* df = (AstFunction *) nn;
-            AstFunction* sf = (AstFunction *) node;
-
-            convert_polyproc_to_function(df);
-
-            if (sf->is_foreign) return node;
-            assert(df->scope == NULL);
-
-            df->nodes_that_need_entities_after_clone = NULL;
-            bh_arr_new(global_heap_allocator, df->nodes_that_need_entities_after_clone, 1);
-            captured_entities = df->nodes_that_need_entities_after_clone;
-
-            df->return_type = (AstType *) ast_clone(a, sf->return_type);
-            df->body = (AstBlock *) ast_clone(a, sf->body);
-
-            df->nodes_that_need_entities_after_clone = captured_entities;
-            captured_entities = NULL;
-
-            df->params = NULL;
-            bh_arr_new(context.ast_alloc, df->params, bh_arr_length(sf->params));
-
-            bh_arr_each(AstParam, param, sf->params) {
-                AstParam new_param = { 0 };
-
-                dont_copy_structs = 1;
-                new_param.local = (AstLocal *) ast_clone(a, param->local);
-                new_param.local->flags &= ~Ast_Flag_Param_Symbol_Dirty;
-                new_param.default_value = (AstTyped *) ast_clone(a, param->default_value);
-                new_param.use_processed = 0;
-                dont_copy_structs = 0;
-
-                new_param.vararg_kind = param->vararg_kind;
-                new_param.is_used = param->is_used;
-                bh_arr_push(df->params, new_param);
-            }
-
-            if (sf->constraints.constraints) {
-                memset(&df->constraints, 0, sizeof(ConstraintContext));
-                bh_arr_new(context.ast_alloc, df->constraints.constraints, bh_arr_length(sf->constraints.constraints));
-
-                bh_arr_each(AstConstraint *, constraint, sf->constraints.constraints) {
-                    bh_arr_push(df->constraints.constraints, (AstConstraint *) ast_clone(a, (AstNode *) *constraint));
-                }
-            }
-
-            if (sf->tags) {
-                bh_arr_new(context.ast_alloc, df->tags, bh_arr_length(sf->tags));
-                bh_arr_each(AstTyped *, pexpr, sf->tags) {
-                    bh_arr_push(df->tags, (AstTyped *) ast_clone(a, (AstNode *) *pexpr));
-                }    
-            }
-
-            break;
-        }
-
-        case Ast_Kind_Constraint: {
-            C(AstConstraint, interface);
-
-            AstConstraint* dc = (AstConstraint *) nn;
-            AstConstraint* sc = (AstConstraint *) node;
-
-            dc->type_args = NULL;
-            bh_arr_new(global_heap_allocator, dc->type_args, bh_arr_length(sc->type_args));
-
-            bh_arr_each(AstType *, type_arg, sc->type_args) {
-                bh_arr_push(dc->type_args, (AstType *) ast_clone(a, (AstNode *) *type_arg));
-            }
-
-            dc->phase = Constraint_Phase_Waiting_To_Be_Queued;
-            break;
-        }
-
-        case Ast_Kind_Use:
-            C(AstUse, expr);
-            break;
-
-        case Ast_Kind_Directive_Solidify: {
-            AstDirectiveSolidify* dd = (AstDirectiveSolidify *) nn;
-            AstDirectiveSolidify* sd = (AstDirectiveSolidify *) node;
-
-            dd->poly_proc = (AstFunction *) ast_clone(a, (AstNode *) sd->poly_proc);
-            dd->resolved_proc = NULL;
-
-            dd->known_polyvars = NULL;
-            bh_arr_new(global_heap_allocator, dd->known_polyvars, bh_arr_length(sd->known_polyvars));
-
-            bh_arr_each(AstPolySolution, sln, sd->known_polyvars) {
-                AstPolySolution new_sln;
-                new_sln.kind     = sln->kind;
-                new_sln.poly_sym = (AstNode *) ast_clone(a, (AstNode *) sln->poly_sym);
-                new_sln.ast_type = (AstType *) ast_clone(a, (AstNode *) sln->ast_type);
-                bh_arr_push(dd->known_polyvars, new_sln);
-            }
-
-            break;
-        }
-
-        case Ast_Kind_Compound: {
-            AstCompound* cd = (AstCompound *) nn;
-            AstCompound* cs = (AstCompound *) node;
-
-            cd->exprs = NULL;
-            bh_arr_new(global_heap_allocator, cd->exprs, bh_arr_length(cs->exprs));
-
-            bh_arr_each(AstTyped *, expr, cs->exprs) {
-                bh_arr_push(cd->exprs, (AstTyped *) ast_clone(a, (AstNode *) *expr));
-            }
-            break;
-        }
-
-        case Ast_Kind_Named_Value:
-            C(AstNamedValue, value);
-            break;
-
-        case Ast_Kind_If_Expression:
-            C(AstIfExpression, cond);
-            C(AstIfExpression, true_expr);
-            C(AstIfExpression, false_expr);
-            break;
-
-        case Ast_Kind_Directive_Insert:
-            C(AstDirectiveInsert, code_expr);
-            break;
-
-        case Ast_Kind_Directive_Defined:
-            C(AstDirectiveDefined, expr);
-            ((AstDirectiveDefined *) nn)->is_defined = 0;
-            break;
-
-        case Ast_Kind_Typeof:
-            C(AstTypeOf, expr);
-            ((AstTypeOf *) nn)->resolved_type = NULL;
-            break;
-
-        case Ast_Kind_Do_Block:
-            C(AstDoBlock, block);
-            ((AstDoBlock *) nn)->type_node = (AstType *) &basic_type_auto_return;
-            break;
-
-        case Ast_Kind_File_Contents:
-            C(AstFileContents, filename_expr);
-            E(nn);
-            break;
-    }
-
-    clone_depth--;
-    return nn;
-}
-
-#undef C
-
-AstFunction* clone_function_header(bh_allocator a, AstFunction* func) {
-    if (func->kind != Ast_Kind_Function && func->kind != Ast_Kind_Polymorphic_Proc) return NULL;
-
-    if (func->is_foreign) return func;
-
-    AstFunction* new_func = onyx_ast_node_new(a, sizeof(AstFunction), func->kind);
-    memmove(new_func, func, sizeof(AstFunction));
-    assert(new_func->scope == NULL);
-
-    convert_polyproc_to_function(new_func);
-
-    new_func->return_type = (AstType *) ast_clone(a, func->return_type);
-
-    new_func->params = NULL;
-    bh_arr_new(global_heap_allocator, new_func->params, bh_arr_length(func->params));
-    bh_arr_each(AstParam, param, func->params) {
-        AstParam new_param;
-
-        dont_copy_structs = 1;
-        new_param.local = (AstLocal *) ast_clone(a, param->local);
-        new_param.local->flags &= ~Ast_Flag_Param_Symbol_Dirty;
-        new_param.default_value = (AstTyped *) ast_clone(a, param->default_value);
-        new_param.use_processed = 0;
-        dont_copy_structs = 0;
-
-        new_param.vararg_kind = param->vararg_kind;
-        new_param.is_used = param->is_used;
-        bh_arr_push(new_func->params, new_param);
-    }
-
-    if (func->constraints.constraints) {
-        memset(&new_func->constraints, 0, sizeof(ConstraintContext));
-        bh_arr_new(global_heap_allocator, new_func->constraints.constraints, bh_arr_length(func->constraints.constraints));
-
-        bh_arr_each(AstConstraint *, constraint, func->constraints.constraints) {
-            bh_arr_push(new_func->constraints.constraints, (AstConstraint *) ast_clone(a, (AstNode *) *constraint));
-        }
-    }
-
-    return new_func;
-}
-
-// Clones a function body from a given function. It is assumed that `dest` is
-// a function from `clone_function_header`.
-void clone_function_body(bh_allocator a, AstFunction* dest, AstFunction* source) {
-    if (dest->kind != Ast_Kind_Function) return;
-    if (source->kind != Ast_Kind_Polymorphic_Proc && source->kind != Ast_Kind_Function) return;
-
-    dest->nodes_that_need_entities_after_clone = NULL;
-    bh_arr_new(global_heap_allocator, dest->nodes_that_need_entities_after_clone, 1);
-    captured_entities = dest->nodes_that_need_entities_after_clone;
-
-    dest->body = (AstBlock *) ast_clone(a, source->body);
-    
-    dest->nodes_that_need_entities_after_clone = captured_entities;
-    captured_entities = NULL;
-}
diff --git a/src/doc.c b/src/doc.c
deleted file mode 100644 (file)
index 2a8bbd4..0000000
--- a/src/doc.c
+++ /dev/null
@@ -1,292 +0,0 @@
-#include "doc.h"
-#include "utils.h"
-#include "types.h"
-
-static i32 cmp_doc_entry(const void * a, const void * b) {
-    DocEntry* d1 = (DocEntry *) a;
-    DocEntry* d2 = (DocEntry *) b;
-
-    return strncmp(d1->def, d2->def, 1024);
-}
-
-static i32 cmp_doc_package(const void * a, const void * b) {
-    DocPackage* d1 = (DocPackage *) a;
-    DocPackage* d2 = (DocPackage *) b;
-
-    return strncmp(d1->name, d2->name, 1024);
-}
-
-static char* node_to_doc_def(const char* sym, AstNode *node, bh_allocator a) {
-    static char buf[1024];
-    memset(buf, 0, 1023);
-
-    strncat(buf, sym, 1023);
-    strncat(buf, " :: ", 1023);
-
-    switch (node->kind) {
-        case Ast_Kind_Function: {
-            strncat(buf, "(", 1023);
-
-            AstFunction *func = (AstFunction *) node;
-            bh_arr_each(AstParam, param, func->params) {
-                if (param != func->params)
-                    strncat(buf, ", ", 1023);
-
-                token_toggle_end(param->local->token);
-                strncat(buf, param->local->token->text, 1023);
-                token_toggle_end(param->local->token);
-
-                strncat(buf, ": ", 1023);
-
-                strncat(buf, type_get_name(param->local->type), 1023);
-
-                if (param->default_value != NULL) {
-                    strncat(buf, " = <default>", 1023);
-                }
-            }
-
-            strncat(buf, ") -> ", 1023);
-            strncat(buf, type_get_name(func->type->Function.return_type), 1023);
-
-            break;
-        }
-
-        case Ast_Kind_Struct_Type: {
-            strncat(buf, "struct { ", 1023);
-
-            AstStructType* st = (AstStructType *) node;
-            bh_arr_each(AstStructMember *, smem, st->members) {
-
-                token_toggle_end((*smem)->token);
-                strncat(buf, (*smem)->token->text, 1023);
-                token_toggle_end((*smem)->token);
-
-                strncat(buf, ": ", 1023);
-
-                strncat(buf, type_get_name((*smem)->type), 1023);
-
-                strncat(buf, "; ", 1023);
-            }
-
-            strncat(buf, "}", 1023);
-            break;
-        }
-
-        case Ast_Kind_Basic_Type:
-        case Ast_Kind_Array_Type:
-        case Ast_Kind_Type_Alias:
-        case Ast_Kind_Slice_Type:
-        case Ast_Kind_DynArr_Type: {
-            strncat(buf, type_get_name(type_build_from_ast(global_heap_allocator, (AstType *) node)), 1023);
-            break;
-        }
-
-        default: {
-            strncat(buf, "<unimplemented printing>", 1023);
-        }
-    }
-
-    return bh_strdup(a, buf);
-}
-
-static DocPackage doc_package_create(Package* p, bh_allocator a) {
-    DocPackage dp;
-    dp.name = p->name;
-    dp.public_entries = NULL;
-    dp.private_entries = NULL;
-
-    bh_arr_new(global_heap_allocator, dp.public_entries, 16);
-    bh_arr_new(global_heap_allocator, dp.private_entries, 16);
-
-    fori (i, 0, shlen(p->scope->symbols)) {
-        char *key = p->scope->symbols[i].key;
-        AstNode *value = p->scope->symbols[i].value;
-
-        DocEntry de;
-        if (value->token) de.pos = value->token->pos;
-        de.def = node_to_doc_def(key, value, a);
-        de.sym = (char *) key;
-        de.additional = NULL;
-
-        bh_arr_push(dp.public_entries, de);
-    }
-
-    fori (i, 0, shlen(p->private_scope->symbols)) {
-        char *key = p->scope->symbols[i].key;
-        AstNode *value = p->scope->symbols[i].value;
-
-        DocEntry de;
-        if (value->token) de.pos = value->token->pos;
-        de.def = node_to_doc_def(key, value, a);
-        de.sym = (char *) key;
-        de.additional = NULL;
-
-        bh_arr_push(dp.private_entries, de);
-    }
-
-    qsort(dp.public_entries,  bh_arr_length(dp.public_entries),  sizeof(DocEntry), cmp_doc_entry);
-    qsort(dp.private_entries, bh_arr_length(dp.private_entries), sizeof(DocEntry), cmp_doc_entry);
-
-    return dp;
-}
-
-OnyxDocumentation onyx_docs_generate() {
-    OnyxDocumentation doc;
-
-    bh_arena_init(&doc.doc_arena, global_heap_allocator, 16 * 1024);
-    bh_allocator a = bh_arena_allocator(&doc.doc_arena);
-
-    doc.package_docs = NULL;
-    bh_arr_new(global_heap_allocator, doc.package_docs, 16);
-
-    fori (i, 0, shlen(context.packages)) {
-        DocPackage dp = doc_package_create(context.packages[i].value, a);
-        bh_arr_push(doc.package_docs, dp);
-    }
-
-    qsort(doc.package_docs, bh_arr_length(doc.package_docs), sizeof(DocPackage), cmp_doc_package);
-
-    return doc;
-}
-
-static void onyx_docs_emit_human(OnyxDocumentation* doc, bh_file* file) {
-    // NOTE: Disabling fancy line printing until I can make it better
-    #if 0
-    bh_arr_each(DocPackage, dp, doc->package_docs) {
-        bh_printf("Package '%s'\n", dp->name);
-
-        if (bh_arr_length(dp->public_entries) > 0) {
-            bh_printf("\xe2\x94\x9c\xe2\x94\x80 Public symbols\n");
-            bh_arr_each(DocEntry, de, dp->public_entries) {
-                bh_printf("\xe2\x94\x82  \xe2\x94\x9c\xe2\x94\x80 %s\n", de->def);
-
-                if (de->pos.filename != NULL)
-                    bh_printf("\xe2\x94\x82  \xe2\x94\x82    at %s:%d,%d\n", de->pos.filename, de->pos.line, de->pos.column);
-                else
-                    bh_printf("\xe2\x94\x82  \xe2\x94\x82    compiler built-in\n");
-
-                bh_printf("\xe2\x94\x82  \xe2\x94\x82\n");
-            }
-        }
-
-        if (bh_arr_length(dp->private_entries) > 0) {
-            bh_printf("\xe2\x94\x9c\xe2\x94\x80 Private symbols\n");
-            bh_arr_each(DocEntry, de, dp->private_entries) {
-                bh_printf("\xe2\x94\x82  \xe2\x94\x9c\xe2\x94\x80 %s\n", de->def);
-                if (de->pos.filename != NULL)
-                    bh_printf("\xe2\x94\x82  \xe2\x94\x82    at %s:%d,%d\n", de->pos.filename, de->pos.line, de->pos.column);
-                else
-                    bh_printf("\xe2\x94\x82  \xe2\x94\x82    compiler built-in\n");
-
-                bh_printf("\xe2\x94\x82  \xe2\x94\x82\n");
-            }
-
-            bh_printf("\n");
-        }
-    }
-    #else
-    bh_arr_each(DocPackage, dp, doc->package_docs) {
-        bh_fprintf(file, "Package '%s'\n", dp->name);
-
-        if (bh_arr_length(dp->public_entries) > 0) {
-            bh_fprintf(file, "   Public symbols\n");
-            bh_arr_each(DocEntry, de, dp->public_entries) {
-                bh_fprintf(file, "     %s\n", de->def);
-
-                if (de->pos.filename != NULL)
-                    bh_fprintf(file, "        at %s:%d,%d\n", de->pos.filename, de->pos.line, de->pos.column);
-                else
-                    bh_fprintf(file, "        compiler built-in\n");
-
-                bh_fprintf(file, "    \n");
-            }
-        }
-
-        if (bh_arr_length(dp->private_entries) > 0) {
-            bh_fprintf(file, "   Private symbols\n");
-            bh_arr_each(DocEntry, de, dp->private_entries) {
-                bh_fprintf(file, "      %s\n", de->def);
-                if (de->pos.filename != NULL)
-                    bh_fprintf(file, "        at %s:%d,%d\n", de->pos.filename, de->pos.line, de->pos.column);
-                else
-                    bh_fprintf(file, "        compiler built-in\n");
-
-                bh_fprintf(file, "    \n");
-            }
-
-            bh_fprintf(file, "\n");
-        }
-    }
-    #endif
-}
-
-
-static i32 sort_tags(const void* a, const void* b) {
-    DocEntry *da = *((DocEntry **) a);
-    DocEntry *db = *((DocEntry **) b);
-
-    return strcmp(da->sym, db->sym);
-}
-
-static void onyx_docs_emit_tags(OnyxDocumentation* doc) {
-    bh_file tags_file;
-    if (bh_file_create(&tags_file, "./tags") != BH_FILE_ERROR_NONE) {
-        bh_printf("Cannot create 'tags'.\n");
-        return;
-    }
-
-    bh_fprintf(&tags_file, "!_TAG_FILE_FORMAT\t2\n");
-    bh_fprintf(&tags_file, "!_TAG_FILE_SORTED\t1\n");
-    bh_fprintf(&tags_file, "!_TAG_OUTPUT_FILESEP\tslash\n");
-    bh_fprintf(&tags_file, "!_TAG_OUTPUT_MODE\tu-ctags\n");
-    bh_fprintf(&tags_file, "!_TAG_PROGRAM_AUTHOR\tOnyx Compiler\n");
-    bh_fprintf(&tags_file, "!_TAG_PROGRAM_NAME\tOnyx Compiler\n");
-    bh_fprintf(&tags_file, "!_TAG_PROGRAM_URL\thttps://github.com/brendanfh/onyx\n");
-    bh_fprintf(&tags_file, "!_TAG_PROGRAM_VERSION\t0.0.1\n");
-
-    bh_arr(DocEntry *) tags = NULL;
-    bh_arr_new(global_heap_allocator, tags, 256);
-
-    bh_arr_each(DocPackage, dp, doc->package_docs) {
-        bh_arr_each(DocEntry, de, dp->public_entries) bh_arr_push(tags, de);
-        bh_arr_each(DocEntry, de, dp->private_entries) bh_arr_push(tags, de);
-    }
-
-    qsort(tags, bh_arr_length(tags), sizeof(DocEntry *), sort_tags);
-
-    bh_arr_each(DocEntry *, tag, tags) {
-        if ((*tag)->pos.filename == NULL) continue;
-
-        i32 line_len = 0;
-        char *c = (*tag)->pos.line_start;
-        while (*c++ != '\n') line_len++;
-
-        bh_fprintf(&tags_file, "%s\t%s\t/^%b$/\n",
-                (*tag)->sym, (*tag)->pos.filename,
-                (*tag)->pos.line_start, line_len);
-    }
-
-    bh_file_close(&tags_file);
-}
-
-static void onyx_docs_emit_html(OnyxDocumentation* doc, bh_file* file) {
-    bh_fprintf(file, "HTML documentation output not supported yet.\n");
-    return;
-}
-
-void onyx_docs_emit(OnyxDocumentation* doc, const char* filename) {
-    bh_file file;
-    if (bh_file_create(&file, filename) != BH_FILE_ERROR_NONE) {
-        bh_printf("ERROR: Failed to open file '%s' for writing documentation.\n", filename);
-        return;
-    }
-
-    switch (doc->format) {
-        case Doc_Format_Human: onyx_docs_emit_human(doc, &file); break;
-        case Doc_Format_Html: onyx_docs_emit_html(doc, &file); break;
-        case Doc_Format_Tags: onyx_docs_emit_tags(doc); break;
-    }
-
-    bh_file_close(&file);
-}
-
diff --git a/src/entities.c b/src/entities.c
deleted file mode 100644 (file)
index 0ceb2f9..0000000
+++ /dev/null
@@ -1,406 +0,0 @@
-#include "bh.h"
-#include "astnodes.h"
-#include "utils.h"
-
-static inline i32 entity_phase(Entity* e1) {
-    if (e1->state <= Entity_State_Parse && e1->macro_attempts == 0) return 1;
-    if (e1->state <  Entity_State_Code_Gen) return 2;
-    return 3;
-}
-
-// NOTE: Returns >0 if e1 should be processed after e2.
-static i32 entity_compare(Entity* e1, Entity* e2) {
-    i32 phase1 = entity_phase(e1);
-    i32 phase2 = entity_phase(e2);
-
-    if (phase1 != phase2)
-        return phase1 - phase2;
-    else if (e1->macro_attempts != e2->macro_attempts)
-        return (i32) e1->macro_attempts - (i32) e2->macro_attempts;
-    else if (e1->state != e2->state)
-        return (i32) e1->state - (i32) e2->state;
-    else if (e1->type != e2->type)
-        return (i32) e1->type - (i32) e2->type;
-    else if (e1->micro_attempts != e2->micro_attempts)
-        return (i32) (e1->micro_attempts - e2->micro_attempts);
-    else
-        return (i32) (e1->id - e2->id);
-}
-
-#define eh_parent(index) (((index) - 1) / 2)
-#define eh_lchild(index) (((index) * 2) + 1)
-#define eh_rchild(index) (((index) * 2) + 2)
-
-static void eh_shift_up(EntityHeap* entities, i32 index) {
-    while (index > 0 && entity_compare(entities->entities[eh_parent(index)], entities->entities[index]) > 0) {
-        Entity* tmp = entities->entities[eh_parent(index)];
-        entities->entities[eh_parent(index)] = entities->entities[index];
-        entities->entities[index] = tmp;
-
-        index = eh_parent(index);
-    }
-}
-
-static void eh_shift_down(EntityHeap* entities, i32 index) {
-    while (1) {
-        i32 min_index = index;
-
-        i32 l = eh_lchild(index);
-        if (l < bh_arr_length(entities->entities)
-            && entity_compare(entities->entities[l], entities->entities[min_index]) < 0) {
-            min_index = l;
-        }
-
-        i32 r = eh_rchild(index);
-        if (r < bh_arr_length(entities->entities)
-            && entity_compare(entities->entities[r], entities->entities[min_index]) < 0) {
-            min_index = r;
-        }
-
-        if (index != min_index) {
-            Entity* tmp = entities->entities[min_index];
-            entities->entities[min_index] = entities->entities[index];
-            entities->entities[index] = tmp;
-
-            index = min_index;
-            continue;
-        }
-
-        break;
-    }
-}
-
-void entity_heap_init(EntityHeap* entities) {
-    bh_arena_init(&entities->entity_arena, global_heap_allocator, 32 * 1024);
-}
-
-static u32 next_entity_id = 0;
-
-// Allocates the entity in the entity heap. Don't quite feel this is necessary...
-Entity* entity_heap_register(EntityHeap* entities, Entity e) {
-    bh_allocator alloc = bh_arena_allocator(&entities->entity_arena);
-    Entity* entity = bh_alloc_item(alloc, Entity);
-    *entity = e;
-    entity->id = next_entity_id++;
-    entity->macro_attempts = 0;
-    entity->micro_attempts = 0;
-    entity->entered_in_queue = 0;
-
-    return entity;
-}
-
-void entity_heap_insert_existing(EntityHeap* entities, Entity* e) {
-    if (e->entered_in_queue) return;
-
-    if (entities->entities == NULL) {
-        bh_arr_new(global_heap_allocator, entities->entities, 128);
-    }
-
-    bh_arr_push(entities->entities, e);
-    eh_shift_up(entities, bh_arr_length(entities->entities) - 1);
-    e->entered_in_queue = 1;
-
-    entities->state_count[e->state]++;
-    entities->type_count[e->type]++;
-    entities->all_count[e->state][e->type]++;
-}
-
-Entity* entity_heap_insert(EntityHeap* entities, Entity e) {
-    Entity* entity = entity_heap_register(entities, e);
-    entity_heap_insert_existing(entities, entity);
-    return entity;
-}
-
-Entity* entity_heap_top(EntityHeap* entities) {
-    return entities->entities[0];
-}
-
-void entity_heap_change_top(EntityHeap* entities, Entity* new_top) {
-    entities->state_count[entities->entities[0]->state]--;
-    entities->state_count[new_top->state]++;
-
-    entities->type_count[entities->entities[0]->type]--;
-    entities->type_count[new_top->type]++;
-
-    entities->all_count[entities->entities[0]->state][entities->entities[0]->type]--;
-    entities->all_count[new_top->state][new_top->type]++;
-
-    entities->entities[0] = new_top;
-    eh_shift_down(entities, 0);
-}
-
-void entity_heap_remove_top(EntityHeap* entities) {
-    entities->state_count[entities->entities[0]->state]--;
-    entities->type_count[entities->entities[0]->type]--;
-    entities->all_count[entities->entities[0]->state][entities->entities[0]->type]--;
-    entities->entities[0]->entered_in_queue = 0;
-
-    entities->entities[0] = entities->entities[bh_arr_length(entities->entities) - 1];
-    bh_arr_pop(entities->entities);
-    eh_shift_down(entities, 0);
-}
-
-void entity_change_type(EntityHeap* entities, Entity *ent, EntityType new_type) {
-    entities->type_count[ent->type]--;
-    entities->type_count[new_type]++;
-    ent->type = new_type;
-}
-
-// NOTE(Brendan Hansen): Uses the entity heap in the context structure
-void add_entities_for_node(bh_arr(Entity *) *target_arr, AstNode* node, Scope* scope, Package* package) {
-#define ENTITY_INSERT(_ent)                                     \
-    entity = entity_heap_register(entities, _ent);              \
-    if (target_arr) {                                           \
-        bh_arr(Entity *) __tmp_arr = *target_arr;               \
-        bh_arr_push(__tmp_arr, entity);                         \
-        *target_arr = __tmp_arr;                                \
-    } else {                                                    \
-        entity_heap_insert_existing(entities, entity);          \
-    }                                                           \
-
-    if (node->entity != NULL) return;
-
-    EntityHeap* entities = &context.entities;
-    Entity* entity;
-
-    Entity ent;
-    ent.id = entities->next_id++;
-    ent.state = Entity_State_Resolve_Symbols;
-    ent.package = package;
-    ent.scope   = scope;
-
-    switch (node->kind) {
-        case Ast_Kind_Load_All:
-        case Ast_Kind_Load_File: {
-            ent.type = Entity_Type_Load_File;
-            ent.include = (AstInclude *) node;
-            ENTITY_INSERT(ent);
-            break;
-        }
-
-        case Ast_Kind_Library_Path:
-        case Ast_Kind_Load_Path: {
-            ent.type = Entity_Type_Load_Path;
-            ent.include = (AstInclude *) node;
-            ENTITY_INSERT(ent);
-            break;
-        }
-
-        case Ast_Kind_Binding: {
-            ent.state   = Entity_State_Introduce_Symbols;
-            ent.type    = Entity_Type_Binding;
-            ent.binding = (AstBinding *) node;
-            ENTITY_INSERT(ent);
-            break;
-        }
-
-        case Ast_Kind_Function: {
-            if (((AstFunction *) node)->is_foreign != 0) {
-                ent.type     = Entity_Type_Foreign_Function_Header;
-                ent.function = (AstFunction *) node;
-                ENTITY_INSERT(ent);
-
-            } else {
-                ent.type     = Entity_Type_Function_Header;
-                ent.function = (AstFunction *) node;
-                ENTITY_INSERT(ent);
-                ((AstFunction *) node)->entity_header = entity;
-
-                ent.id       = entities->next_id++;
-                ent.type     = Entity_Type_Function;
-                ent.function = (AstFunction *) node;
-                ENTITY_INSERT(ent);
-                ((AstFunction *) node)->entity_body = entity;
-            }
-            break;
-        }
-
-        case Ast_Kind_Overloaded_Function: {
-            ent.type                = Entity_Type_Overloaded_Function;
-            ent.overloaded_function = (AstOverloadedFunction *) node;
-            ENTITY_INSERT(ent);
-            break;
-        }
-
-        case Ast_Kind_Global: {
-            ent.type   = Entity_Type_Global_Header;
-            ent.global = (AstGlobal *) node;
-            ENTITY_INSERT(ent);
-
-            ent.id     = entities->next_id++;
-            ent.type   = Entity_Type_Global;
-            ent.global = (AstGlobal *) node;
-            ENTITY_INSERT(ent);
-            break;
-        }
-
-        case Ast_Kind_StrLit: {
-            ent.type   = Entity_Type_String_Literal;
-            ent.strlit = (AstStrLit *) node;
-            ENTITY_INSERT(ent);
-            break;
-        }
-
-        case Ast_Kind_File_Contents: {
-            ent.type = Entity_Type_File_Contents;
-            ent.file_contents = (AstFileContents *) node;
-            ENTITY_INSERT(ent);
-            break;
-        }
-
-        case Ast_Kind_Struct_Type: {
-            ent.type = Entity_Type_Struct_Member_Default;
-            ent.type_alias = (AstType *) node;
-            ENTITY_INSERT(ent);
-            ((AstStructType *) node)->entity_defaults = entity;
-
-            ent.id       = entities->next_id++;
-            // fallthrough
-        }
-
-        case Ast_Kind_Poly_Struct_Type:
-        case Ast_Kind_Type_Alias: {
-            ent.type = Entity_Type_Type_Alias;
-            ent.type_alias = (AstType *) node;
-            ENTITY_INSERT(ent);
-
-            if (node->kind == Ast_Kind_Struct_Type) {
-                ((AstStructType *) node)->entity_type = entity;
-            }
-
-            break;
-        }
-
-        case Ast_Kind_Enum_Type: {
-            ent.type = Entity_Type_Enum;
-            ent.enum_type = (AstEnumType *) node;
-            ENTITY_INSERT(ent);
-            break;
-        }
-
-        case Ast_Kind_Enum_Value: {
-            ent.type = Entity_Type_Enum_Value;
-            ent.state = Entity_State_Check_Types;
-            ent.enum_value = (AstEnumValue *) node;
-            ENTITY_INSERT(ent);
-            break;
-        }
-
-        case Ast_Kind_Use: {
-            if (((AstUse *) node)->expr->kind == Ast_Kind_Package) {
-                ent.state = Entity_State_Resolve_Symbols;
-                ent.type = Entity_Type_Use_Package;
-            } else {
-                ent.type = Entity_Type_Use;
-            }
-
-            ent.use = (AstUse *) node;
-            ENTITY_INSERT(ent);
-            break;
-        }
-
-        case Ast_Kind_Memres: {
-            ent.type = Entity_Type_Memory_Reservation_Type;
-            ent.mem_res = (AstMemRes *) node;
-            ENTITY_INSERT(ent);
-            ((AstMemRes *) node)->type_entity = entity;
-
-            ent.id       = entities->next_id++;
-            ent.type = Entity_Type_Memory_Reservation;
-            ent.mem_res = (AstMemRes *) node;
-            ENTITY_INSERT(ent);
-            break;
-        }
-
-        case Ast_Kind_Macro: {
-            ent.type = Entity_Type_Macro;
-            ent.macro = (AstMacro *) node;
-            ENTITY_INSERT(ent);
-            break;
-        }
-
-        case Ast_Kind_Polymorphic_Proc: {
-            ent.type = Entity_Type_Polymorphic_Proc;
-            ent.poly_proc = (AstFunction *) node;
-            ENTITY_INSERT(ent);
-            break;
-        }
-
-        case Ast_Kind_Polymorph_Query: {
-            ent.type = Entity_Type_Polymorph_Query;
-            ent.state = Entity_State_Check_Types;
-            ent.poly_query = (AstPolyQuery *) node;
-            ENTITY_INSERT(ent);
-            break;
-        }
-
-        case Ast_Kind_Static_If: {
-            ent.state = Entity_State_Resolve_Symbols;
-            ent.type = Entity_Type_Static_If;
-            ent.static_if = (AstIf *) node;
-            ENTITY_INSERT(ent);
-            break;
-        }
-
-        case Ast_Kind_Directive_Error: {
-            ent.state = Entity_State_Error;
-            ent.type = Entity_Type_Error;
-            ent.error = (AstDirectiveError *) node;
-            ENTITY_INSERT(ent);
-            break;
-        }
-
-        case Ast_Kind_Directive_Export:
-        case Ast_Kind_Directive_Add_Overload:
-        case Ast_Kind_Directive_Operator:
-        case Ast_Kind_Directive_Init:
-        case Ast_Kind_Directive_Library:
-        case Ast_Kind_Injection: {
-            ent.type = Entity_Type_Process_Directive;
-            ent.expr = (AstTyped *) node;
-            ENTITY_INSERT(ent);
-            break;
-        }
-
-        case Ast_Kind_Note: {
-            ent.type = Entity_Type_Note;
-            ent.expr = (AstTyped *) node;
-            ent.state = Entity_State_Code_Gen;
-            ENTITY_INSERT(ent);
-            break;
-        }
-
-        case Ast_Kind_Interface: {
-            ent.type = Entity_Type_Interface;
-            ent.interface = (AstInterface *) node;
-            ent.state = Entity_State_Resolve_Symbols;
-            ENTITY_INSERT(ent);
-            break;
-        }
-
-        case Ast_Kind_Constraint: {
-            ent.type = Entity_Type_Constraint_Check;
-            ent.constraint = (AstConstraint *) node;
-            ent.state = Entity_State_Resolve_Symbols;
-            ENTITY_INSERT(ent);
-            break;
-        }
-
-        case Ast_Kind_Foreign_Block: {
-            ent.type = Entity_Type_Foreign_Block;
-            ent.foreign_block = (AstForeignBlock *) node;
-            ent.state = Entity_State_Resolve_Symbols;
-            ENTITY_INSERT(ent);
-            break;
-        }
-
-        default: {
-            ent.type = Entity_Type_Expression;
-            ent.expr = (AstTyped *) node;
-            ENTITY_INSERT(ent);
-            break;
-        }
-    }
-
-    node->entity = entity;
-}
diff --git a/src/errors.c b/src/errors.c
deleted file mode 100644 (file)
index f6811ca..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-#include "errors.h"
-#include "utils.h"
-
-OnyxErrors errors;
-
-void onyx_errors_init(bh_arr(bh_file_contents)* files) {
-    errors.file_contents = files;
-
-    bh_arena_init(&errors.msg_arena, global_heap_allocator, 16 * 1024);
-    errors.msg_alloc = bh_arena_allocator(&errors.msg_arena);
-
-    bh_arr_new(global_heap_allocator, errors.errors, 4);
-}
-
-static void print_detailed_message(OnyxError* err, bh_file_contents* fc) {
-    bh_printf("(%s:%l,%l) %s\n", err->pos.filename, err->pos.line, err->pos.column, err->text);
-
-    b32 colored_printing = 0;
-    #ifdef _BH_LINUX
-        colored_printing = !context.options->no_colors;
-    #endif
-
-    i32 linelength = 0;
-    i32 first_char = 0;
-    char* walker = err->pos.line_start;
-    while (*walker == ' ' || *walker == '\t') first_char++, linelength++, walker++;
-    while (*walker != '\n') linelength++, walker++;
-
-    if (colored_printing) bh_printf("\033[90m");
-    i32 numlen = bh_printf(" %d | ", err->pos.line);
-    if (colored_printing) bh_printf("\033[94m");
-    bh_printf("%b\n", err->pos.line_start, linelength);
-
-    char* pointer_str = bh_alloc_array(global_scratch_allocator, char, linelength + numlen);
-    memset(pointer_str, ' ', linelength + numlen);
-    memcpy(pointer_str + numlen - 1, err->pos.line_start, first_char);
-    memset(pointer_str + first_char + numlen - 1, ' ', err->pos.column - first_char);
-    memset(pointer_str + err->pos.column + numlen - 1, '~', err->pos.length - 1);
-    pointer_str[err->pos.column + numlen - 2] = '^';
-    pointer_str[err->pos.column + numlen + err->pos.length - 1] = 0;
-
-    if (colored_printing) bh_printf("\033[91m");
-    bh_printf("%s\n", pointer_str);
-
-    if (colored_printing) bh_printf("\033[0m");
-}
-
-static i32 errors_sort(const void* v1, const void* v2) {
-    OnyxError* e1 = (OnyxError *) v1;
-    OnyxError* e2 = (OnyxError *) v2;
-    return e2->rank - e1->rank;
-}
-
-void onyx_errors_print() {
-    // NOTE: If the format of the error messages is ever changed,
-    // update onyx_compile.vim and onyx.sublime-build to match
-    // the new format. This was editor error highlighting is still
-    // supported.
-    //
-    //                                      - brendanfh   2020/09/03
-
-    qsort(errors.errors, bh_arr_length(errors.errors), sizeof(OnyxError), errors_sort);
-
-    OnyxErrorRank last_rank = errors.errors[0].rank;
-    bh_arr_each(OnyxError, err, errors.errors) {
-        if (last_rank != err->rank) break;
-
-        if (err->pos.filename) {
-            bh_file_contents file_contents = { 0 };
-            bh_arr_each(bh_file_contents, fc, *errors.file_contents) {
-                if (!strcmp(fc->filename, err->pos.filename)) {
-                    file_contents = *fc;
-                    break;
-                }
-            }
-
-            print_detailed_message(err, &file_contents);
-
-        } else {
-            bh_printf("(%l,%l) %s\n", err->pos.line, err->pos.column, err->text);
-        }
-
-        last_rank = err->rank;
-    }
-}
-
-b32 onyx_has_errors() {
-    return bh_arr_length(errors.errors) > 0;
-}
-
-void onyx_clear_errors() {
-    if (context.cycle_detected) return;
-
-    bh_arr_set_length(errors.errors, 0);
-}
-
-void onyx_submit_error(OnyxError error) {
-    bh_arr_push(errors.errors, error);
-}
-
-void onyx_report_error(OnyxFilePos pos, OnyxErrorRank rank, char * format, ...) {
-
-    va_list vargs;
-    va_start(vargs, format);
-    char* msg = bh_bprintf_va(format, vargs);
-    va_end(vargs);
-
-    OnyxError err = {
-        .pos = pos,
-        .rank = rank,
-        .text = bh_strdup(errors.msg_alloc, msg),
-    };
-
-    bh_arr_push(errors.errors, err);
-}
-
-void onyx_submit_warning(OnyxError error) {
-    bh_file_contents file_contents = { 0 };
-    bh_arr_each(bh_file_contents, fc, *errors.file_contents) {
-        if (!strcmp(fc->filename, error.pos.filename)) {
-            file_contents = *fc;
-            break;
-        }
-    }
-
-    print_detailed_message(&error, &file_contents);
-}
-
-// This definitely doesn't do what I thought it did?
-void onyx_report_warning(OnyxFilePos pos, char* format, ...) {
-    va_list vargs;
-    va_start(vargs, format);
-    char* msg = bh_bprintf_va(format, vargs);
-    va_end(vargs);
-
-    OnyxError err = {
-        .pos = pos,
-        .rank = Error_Warning,
-        .text = msg,
-    };
-
-    bh_arr_push(errors.errors, err);
-
-    /*
-
-    bh_file_contents file_contents = { 0 };
-    bh_arr_each(bh_file_contents, fc, *errors.file_contents) {
-        if (!strcmp(fc->filename, pos.filename)) {
-            file_contents = *fc;
-            break;
-        }
-    }
-
-    print_detailed_message(&err, &file_contents);
-    */
-}
diff --git a/src/lex.c b/src/lex.c
deleted file mode 100644 (file)
index eba315e..0000000
--- a/src/lex.c
+++ /dev/null
@@ -1,548 +0,0 @@
-#include "bh.h"
-#include "lex.h"
-#include "utils.h"
-#include "errors.h"
-
-u64 lexer_lines_processed = 0;
-u64 lexer_tokens_processed = 0;
-
-static const char* token_type_names[] = {
-    "TOKEN_TYPE_UNKNOWN",
-    "TOKEN_TYPE_END_STREAM",
-
-    "TOKEN_TYPE_COMMENT",
-
-    "package",
-    "struct",
-    "enum",
-    "use",
-    "if",
-    "else",
-    "elseif",
-    "return",
-    "global",
-    "cast",
-    "while",
-    "for",
-    "break",
-    "continue",
-    "sizeof",
-    "alignof",
-    "typeof",
-    "defer",
-    "do",
-    "case",
-    "switch",
-    "fallthrough",
-    "macro",
-    "interface",
-    "where",
-
-    "->",
-    "<-",
-    "---",
-    "|>",
-
-    ">=",
-    "<=",
-    "==",
-    "!=",
-    "+=",
-    "-=",
-    "*=",
-    "/=",
-    "%=",
-    "&=",
-    "|=",
-    "^=",
-    "&&",
-    "||",
-    "<<",
-    ">>",
-    ">>>",
-    "<<=",
-    ">>=",
-    ">>>=",
-    "..",
-    "~~",
-
-    "TOKEN_TYPE_SYMBOL",
-    "TOKEN_TYPE_LITERAL_STRING",
-    "TOKEN_TYPE_LITERAL_INTEGER",
-    "TOKEN_TYPE_LITERAL_FLOAT",
-    "true",
-    "false",
-
-    "NOTE",
-
-    "TOKEN_TYPE_COUNT"
-};
-
-#ifndef LITERAL_TOKEN
-#define LITERAL_TOKEN(token, word, token_type) \
-    if (token_lit(tokenizer, &tk, token, word, token_type)) goto token_parsed;
-#endif
-
-#ifndef INCREMENT_CURR_TOKEN
-#define INCREMENT_CURR_TOKEN(tkn) { \
-    if (*(tkn)->curr == '\n') { \
-        (tkn)->line_number++; \
-        (tkn)->line_start = (tkn)->curr + 1; \
-    } \
-    (tkn)->curr++; \
-}
-#endif
-
-#define char_is_alphanum(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z') || ((c) >= '0' && (c) <= '9'))
-#define char_is_num(c)      ((c) >= '0' && (c) <= '9')
-#define char_is_alpha(c)    (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z'))
-
-static inline b32 token_lit(OnyxTokenizer* tokenizer, OnyxToken* tk, char* lit, b32 is_word, TokenType type) {
-    i64 len = 0;
-    char* ptr1 = tokenizer->curr;
-    char* ptr2 = lit;
-    while (*ptr2 != '\0' && *ptr1 == *ptr2) ptr1++, ptr2++, len++;
-    if (*ptr2 != '\0') return 0;
-
-    if (is_word && (char_is_alphanum(*ptr1) || *ptr1 == '_'))
-        return 0;
-
-    tk->type = type;
-    tk->text = tokenizer->curr;
-    tk->length = len;
-    tk->pos.line = tokenizer->line_number;
-    tk->pos.column = (i32)(tokenizer->curr - tokenizer->line_start) + 1;
-
-    tokenizer->curr += len;
-
-    return 1;
-}
-
-const char* token_name(TokenType tkn_type) {
-    if (tkn_type < Token_Type_Ascii_End) {
-        return bh_aprintf(global_scratch_allocator, "%c", (char) tkn_type);
-    } else {
-        return token_type_names[tkn_type - Token_Type_Ascii_End];
-    }
-}
-
-void token_toggle_end(OnyxToken* tkn) {
-    static char backup = 0;
-    char tmp = tkn->text[tkn->length];
-    tkn->text[tkn->length] = backup;
-    backup = tmp;
-}
-
-OnyxToken* onyx_get_token(OnyxTokenizer* tokenizer) {
-    OnyxToken tk;
-
-    // Skip whitespace
-    while (1) {
-        if (tokenizer->curr == tokenizer->end) break;
-
-        switch (*tokenizer->curr) {
-            case ' ':
-            case '\n':
-            case '\t':
-            case '\r':
-                INCREMENT_CURR_TOKEN(tokenizer);
-                break;
-            default:
-                goto whitespace_skipped;
-        }
-    }
-
-whitespace_skipped:
-
-    tk.type = Token_Type_Unknown;
-    tk.text = tokenizer->curr;
-    tk.length = 1;
-    tk.pos.line_start = tokenizer->line_start;
-    tk.pos.filename = tokenizer->filename;
-    tk.pos.line = tokenizer->line_number;
-    tk.pos.column = (u16)(tokenizer->curr - tokenizer->line_start) + 1;
-
-    if (tokenizer->curr == tokenizer->end) {
-        tk.type = Token_Type_End_Stream;
-        goto token_parsed;
-    }
-
-    // She-bang
-    if (tokenizer->curr == tokenizer->start) {
-        if (*tokenizer->curr == '#' && *(tokenizer->curr + 1) == '!') {
-            tk.type = Token_Type_Comment;
-
-            tokenizer->curr += 2;
-            while (*tokenizer->curr++ != '\n' && tokenizer->curr != tokenizer->end);
-
-            tk.length = tokenizer->curr - tk.text;
-            goto token_parsed;
-        }
-    }
-
-    // Comments
-    if (*tokenizer->curr == '/' && *(tokenizer->curr + 1) == '/') {
-        tokenizer->curr += 2;
-        tk.type = Token_Type_Comment;
-        tk.text = tokenizer->curr;
-
-        while (*tokenizer->curr != '\n' && tokenizer->curr != tokenizer->end) {
-            INCREMENT_CURR_TOKEN(tokenizer);
-        }
-
-        tk.length = tokenizer->curr - tk.text - 2;
-        goto token_parsed;
-    }
-
-    if (*tokenizer->curr == '/' && *(tokenizer->curr + 1) == '*') {
-        tokenizer->curr += 2;
-        tk.type = Token_Type_Comment;
-        tk.text = tokenizer->curr;
-
-        i32 comment_depth = 1;
-
-        while (comment_depth > 0 && tokenizer->curr != tokenizer->end) {
-            if (*tokenizer->curr == '/' && *(tokenizer->curr + 1) == '*') {
-                tokenizer->curr += 2;
-                comment_depth += 1;
-            }
-
-            else if (*tokenizer->curr == '*' && *(tokenizer->curr + 1) == '/') {
-                tokenizer->curr += 2;
-                comment_depth -= 1;
-            }
-
-            else {
-                INCREMENT_CURR_TOKEN(tokenizer);
-            }
-        }
-
-        goto token_parsed;
-    }
-
-    if (*tk.text == '"' && *(tk.text + 1) == '"' && *(tk.text + 2) == '"') {
-        u64 len = 0;
-
-        INCREMENT_CURR_TOKEN(tokenizer);
-        INCREMENT_CURR_TOKEN(tokenizer);
-        INCREMENT_CURR_TOKEN(tokenizer);
-
-        while (!(*tokenizer->curr == '"' && *(tokenizer->curr + 1) == '"' && *(tokenizer->curr + 2) == '"')) {
-            len++;
-            INCREMENT_CURR_TOKEN(tokenizer);
-        }
-        
-        INCREMENT_CURR_TOKEN(tokenizer);
-        INCREMENT_CURR_TOKEN(tokenizer);
-        INCREMENT_CURR_TOKEN(tokenizer);
-
-        tk.text += 3;
-        tk.length = len;
-        tk.type = Token_Type_Literal_String;
-        goto token_parsed;
-    }
-
-    // String literal
-    if (*tk.text == '"') {
-        u64 len = 0;
-        u64 slash_count = 0;
-
-        INCREMENT_CURR_TOKEN(tokenizer);
-
-        while (!(*tokenizer->curr == '"' && slash_count == 0)) {
-            len++;
-
-            // if (*tokenizer->curr == '\n') {
-            //     onyx_report_error(tk.pos, "String literal not terminated by end of line.");
-            // }
-
-            if (*tokenizer->curr == '\\') {
-                slash_count += 1;
-                slash_count %= 2;
-            } else {
-                slash_count = 0;
-            }
-
-            INCREMENT_CURR_TOKEN(tokenizer);
-        }
-
-        INCREMENT_CURR_TOKEN(tokenizer);
-
-        tk.text++;
-        tk.type = Token_Type_Literal_String;
-        tk.length = len;
-        goto token_parsed;
-    }
-
-    // Hex literal
-    if (*tokenizer->curr == '0' && *(tokenizer->curr + 1) == 'x' && charset_contains("0123456789abcdefABCDEF", *(tokenizer->curr + 2))) {
-        INCREMENT_CURR_TOKEN(tokenizer);
-        INCREMENT_CURR_TOKEN(tokenizer);
-        u32 len = 3;
-        while (char_is_num(*(tokenizer->curr + 1)) || charset_contains("abcdefABCDEF", *(tokenizer->curr + 1))) {
-            len++;
-            INCREMENT_CURR_TOKEN(tokenizer);
-        }
-
-        tk.type = Token_Type_Literal_Integer;
-        tk.length = len;
-
-        INCREMENT_CURR_TOKEN(tokenizer);
-        goto token_parsed;
-    }
-
-    if (*tokenizer->curr == '@') {
-        INCREMENT_CURR_TOKEN(tokenizer);
-        u32 len = 2;
-        while (char_is_alphanum(*(tokenizer->curr + 1)) || *(tokenizer->curr + 1) == '_') {
-            len++;
-            INCREMENT_CURR_TOKEN(tokenizer);
-        }
-
-        tk.type = Token_Type_Note;
-        tk.length = len;
-        INCREMENT_CURR_TOKEN(tokenizer);
-        goto token_parsed;
-    }
-
-    // Number literal
-    if (char_is_num(*tokenizer->curr)
-        || (*(tokenizer->curr) == '.' && char_is_num(*(tokenizer->curr + 1)))) {
-        tk.type = Token_Type_Literal_Integer;
-
-        b32 hit_decimal = 0;
-        if (*tokenizer->curr == '.') hit_decimal = 1;
-
-        u32 len = 1;
-        while (char_is_num(*(tokenizer->curr + 1))
-            || (!hit_decimal && *(tokenizer->curr + 1) == '.' && *(tokenizer->curr + 2) != '.')) {
-            len++;
-            INCREMENT_CURR_TOKEN(tokenizer);
-
-            if (*tokenizer->curr == '.') hit_decimal = 1;
-        }
-
-        if (*(tokenizer->curr + 1) == 'f') {
-            tk.type = Token_Type_Literal_Float;
-
-            len++;
-            INCREMENT_CURR_TOKEN(tokenizer);
-        }
-
-        if (hit_decimal) tk.type = Token_Type_Literal_Float;
-
-        tk.length = len;
-
-        INCREMENT_CURR_TOKEN(tokenizer);
-        goto token_parsed;
-    }
-
-    char curr = *tokenizer->curr;
-    switch (curr) {
-    case 'a':
-        LITERAL_TOKEN("alignof",     1, Token_Type_Keyword_Alignof);
-        break;
-    case 'b':
-        LITERAL_TOKEN("break",       1, Token_Type_Keyword_Break);
-        break;
-    case 'c':
-        LITERAL_TOKEN("case",        1, Token_Type_Keyword_Case);
-        LITERAL_TOKEN("cast",        1, Token_Type_Keyword_Cast);
-        LITERAL_TOKEN("continue",    1, Token_Type_Keyword_Continue);
-        break;
-    case 'd':
-        LITERAL_TOKEN("defer",       1, Token_Type_Keyword_Defer);
-        LITERAL_TOKEN("do",          1, Token_Type_Keyword_Do);
-        break;
-    case 'e':
-        LITERAL_TOKEN("enum",        1, Token_Type_Keyword_Enum);
-        LITERAL_TOKEN("elseif",      1, Token_Type_Keyword_Elseif);
-        LITERAL_TOKEN("else",        1, Token_Type_Keyword_Else);
-        break;
-    case 'f':
-        LITERAL_TOKEN("for",         1, Token_Type_Keyword_For);
-        LITERAL_TOKEN("false",       1, Token_Type_Literal_False);
-        LITERAL_TOKEN("fallthrough", 1, Token_Type_Keyword_Fallthrough);
-        break;
-    case 'g':
-        LITERAL_TOKEN("global",      1, Token_Type_Keyword_Global);
-        break;
-    case 'i':
-        LITERAL_TOKEN("if",          1, Token_Type_Keyword_If);
-        LITERAL_TOKEN("interface",   1, Token_Type_Keyword_Interface);
-        break;
-    case 'm':
-        LITERAL_TOKEN("macro",       1, Token_Type_Keyword_Macro);
-        break;
-    case 'p':
-        LITERAL_TOKEN("package",     1, Token_Type_Keyword_Package);
-        break;
-    case 'r':
-        LITERAL_TOKEN("return",      1, Token_Type_Keyword_Return);
-        break;
-    case 's':
-        LITERAL_TOKEN("struct",      1, Token_Type_Keyword_Struct);
-        LITERAL_TOKEN("sizeof",      1, Token_Type_Keyword_Sizeof);
-        LITERAL_TOKEN("switch",      1, Token_Type_Keyword_Switch);
-        break;
-    case 't':
-        LITERAL_TOKEN("true",        1, Token_Type_Literal_True);
-        LITERAL_TOKEN("typeof",      1, Token_Type_Keyword_Typeof);
-        break;
-    case 'u':
-        LITERAL_TOKEN("use",         1, Token_Type_Keyword_Use);
-        break;
-    case 'w':
-        LITERAL_TOKEN("while",       1, Token_Type_Keyword_While);
-        LITERAL_TOKEN("where",       1, Token_Type_Keyword_Where);
-        break;
-
-    case '-':
-        LITERAL_TOKEN("->",          0, Token_Type_Right_Arrow);
-        LITERAL_TOKEN("---",         0, Token_Type_Empty_Block);
-        LITERAL_TOKEN("-=",          0, Token_Type_Minus_Equal);
-        break;
-
-    case '<':
-        LITERAL_TOKEN("<=",          0, Token_Type_Less_Equal);
-        LITERAL_TOKEN("<-",          0, Token_Type_Right_Arrow);
-        LITERAL_TOKEN("<<=",         0, Token_Type_Shl_Equal);
-        LITERAL_TOKEN("<<",          0, Token_Type_Shift_Left);
-        break;
-
-    case '>':
-        LITERAL_TOKEN(">=",          0, Token_Type_Greater_Equal);
-        LITERAL_TOKEN(">>>=",        0, Token_Type_Sar_Equal);
-        LITERAL_TOKEN(">>=",         0, Token_Type_Shr_Equal);
-        LITERAL_TOKEN(">>>",         0, Token_Type_Shift_Arith_Right);
-        LITERAL_TOKEN(">>",          0, Token_Type_Shift_Right);
-        break;
-
-    case '&':
-        LITERAL_TOKEN("&&",          0, Token_Type_And_And);
-        LITERAL_TOKEN("&=",          0, Token_Type_And_Equal);
-        break;
-
-    case '|':
-        LITERAL_TOKEN("|>",          0, Token_Type_Pipe);
-        LITERAL_TOKEN("||",          0, Token_Type_Or_Or);
-        LITERAL_TOKEN("|=",          0, Token_Type_Or_Equal);
-        break;
-
-    case '=':
-        LITERAL_TOKEN("==",          0, Token_Type_Equal_Equal);
-        break;
-
-    case '!':
-        LITERAL_TOKEN("!=",          0, Token_Type_Not_Equal);
-        break;
-
-    case '+':
-        LITERAL_TOKEN("+=",          0, Token_Type_Plus_Equal);
-        break;
-
-    case '*':
-        LITERAL_TOKEN("*=",          0, Token_Type_Star_Equal);
-        break;
-
-    case '^':
-        LITERAL_TOKEN("^=",          0, Token_Type_Xor_Equal);
-        break;
-
-    case '/':
-        LITERAL_TOKEN("/=",          0, Token_Type_Fslash_Equal);
-        break;
-
-    case '%':
-        LITERAL_TOKEN("%=",          0, Token_Type_Percent_Equal);
-        break;
-
-    case '.':
-        LITERAL_TOKEN("..",          0, Token_Type_Dot_Dot);
-        break;
-
-    case '~':
-        LITERAL_TOKEN("~~",          0, Token_Type_Tilde_Tilde);
-        break;
-    }
-
-    // Symbols
-    if (char_is_alpha(*tk.text) || *tokenizer->curr == '_') {
-        u64 len = 0;
-        while (char_is_alphanum(*tokenizer->curr) || *tokenizer->curr == '_') {
-            len++;
-            INCREMENT_CURR_TOKEN(tokenizer);
-        }
-
-        tk.length = len;
-        tk.type = Token_Type_Symbol;
-        goto token_parsed;
-    }
-
-
-    tk.type = (TokenType) *tokenizer->curr;
-    INCREMENT_CURR_TOKEN(tokenizer);
-
-token_parsed:
-    tk.pos.length = (u16) tk.length;
-    bh_arr_push(tokenizer->tokens, tk);
-
-    return &tokenizer->tokens[bh_arr_length(tokenizer->tokens) - 1];
-}
-
-OnyxTokenizer onyx_tokenizer_create(bh_allocator allocator, bh_file_contents *fc) {
-    OnyxTokenizer tknizer = {
-        .start          = fc->data,
-        .curr           = fc->data,
-        .end            = bh_pointer_add(fc->data, fc->length),
-
-        .filename       = fc->filename,
-
-        .line_number    = 1,
-        .line_start     = fc->data,
-        .tokens         = NULL,
-    };
-
-    bh_arr_new(allocator, tknizer.tokens, 1 << 12);
-    return tknizer;
-}
-
-void onyx_tokenizer_free(OnyxTokenizer* tokenizer) {
-    bh_arr_free(tokenizer->tokens);
-}
-
-void onyx_lex_tokens(OnyxTokenizer* tokenizer) {
-    OnyxToken* tk;
-    do {
-        tk = onyx_get_token(tokenizer);
-    } while (tk->type != Token_Type_End_Stream);
-
-    lexer_lines_processed += tokenizer->line_number - 1;
-    lexer_tokens_processed += bh_arr_length(tokenizer->tokens);
-}
-
-b32 token_equals(OnyxToken* tkn1, OnyxToken* tkn2) {
-    if (tkn1->length != tkn2->length) return 0;
-    fori (i, 0, tkn1->length)
-        if (tkn1->text[i] != tkn2->text[i]) return 0;
-    return 1;
-}
-
-b32 token_text_equals(OnyxToken* tkn, char* text) {
-    i32 text_len = strlen(text);
-    if (tkn->length != text_len) return 0;
-
-    fori (i, 0, tkn->length)
-        if (tkn->text[i] != text[i]) return 0;
-        
-    return 1;
-}
-
-b32 token_same_file(OnyxToken *tkn1, OnyxToken *tkn2) {
-    if (!tkn1 || !tkn2) return 0;
-    
-    if (tkn1->pos.filename == tkn2->pos.filename) return 1;
-
-    // :Security?
-    return strcmp(tkn1->pos.filename, tkn2->pos.filename) == 0;
-}
diff --git a/src/onyx.c b/src/onyx.c
deleted file mode 100644 (file)
index 9433df3..0000000
+++ /dev/null
@@ -1,800 +0,0 @@
-// #define BH_DEBUG
-#define BH_DEFINE
-#define BH_NO_TABLE
-#define STB_DS_IMPLEMENTATION
-#include "bh.h"
-
-#include "lex.h"
-#include "errors.h"
-#include "parser.h"
-#include "utils.h"
-#include "wasm_emit.h"
-#include "doc.h"
-
-#define VERSION "v0.1.0"
-
-
-// #ifndef CORE_INSTALLATION
-//     #ifdef _BH_LINUX
-//     #define CORE_INSTALLATION "/usr/share/onyx"
-//     #elif defined(_WIN32) || defined(_WIN64)
-//     #define CORE_INSTALLATION "\\dev\\onyx\\"
-//     #endif
-// #endif
-
-
-
-
-Context context;
-
-
-static const char* docstring = "Onyx compiler version " VERSION "\n"
-    "\n"
-    "The compiler for the Onyx programming language, created by Brendan Hansen.\n"
-    "\n"
-    "Usage:\n"
-    "\tonyx compile [-o <target file>] [--verbose] <input files>\n"
-#ifdef ENABLE_RUN_WITH_WASMER
-    "\tonyx run <input files> -- <args>\n"
-#endif
-    // "\tonyx doc <input files>\n"
-    "\tonyx help\n"
-    "\n"
-    "Flags:\n"
-    "\t<input files>           List of initial files\n"
-    "\t-o <target_file>        Specify the target file (default: out.wasm).\n"
-    "\t--runtime, -r <runtime> Specifies the runtime. Can be: onyx, wasi, js, custom.\n"
-    "\t--verbose, -V           Verbose output.\n"
-    "\t           -VV          Very verbose output.\n"
-    "\t           -VVV         Very very verbose output (to be used by compiler developers).\n"
-    "\t--wasm-mvp              Use only WebAssembly MVP features.\n"
-    "\t--multi-threaded        Enables multi-threading for this compilation.\n"
-    "\t--doc <doc_file>\n"
-    "\t--generate-foreign-info\n"
-    "\n"
-    "Developer flags:\n"
-    "\t--print-function-mappings Prints a mapping from WASM function index to source location.\n"
-    "\t--print-static-if-results Prints the conditional result of each #if statement. Useful for debugging.\n"
-    "\t--print-notes             Prints the location of notes throughout the loaded code.\n"
-    "\t--no-colors               Disables colors in the error message.\n"
-    "\t--no-file-contents        Disables '#file_contents' for security.\n"
-    "\n";
-
-
-static CompileOptions compile_opts_parse(bh_allocator alloc, int argc, char *argv[]) {
-    CompileOptions options = {
-        .allocator = alloc,
-        .action = ONYX_COMPILE_ACTION_PRINT_HELP,
-
-        .verbose_output          = 0,
-        .fun_output              = 0,
-        .print_function_mappings = 0,
-        .no_file_contents        = 0,
-
-        .use_post_mvp_features   = 1,
-        .use_multi_threading     = 0,
-
-        .runtime = Runtime_Onyx,
-
-        .files = NULL,
-        .target_file = "out.wasm",
-
-        .documentation_file = NULL,
-
-        .debug_enabled = 0,
-
-        .passthrough_argument_count = 0,
-        .passthrough_argument_data  = NULL,
-    };
-
-    bh_arr_new(alloc, options.files, 2);
-    bh_arr_new(alloc, options.included_folders, 2);
-
-    char* core_installation;
-    #ifdef _BH_LINUX
-    core_installation = CORE_INSTALLATION;
-    #endif
-    #ifdef _BH_WINDOWS
-    core_installation = bh_alloc_array(global_heap_allocator, u8, 512);
-    GetEnvironmentVariableA("ONYX_PATH", core_installation, 512);
-    #endif
-
-    // NOTE: Add the current folder
-    bh_arr_push(options.included_folders, core_installation);
-    bh_arr_push(options.included_folders, ".");
-
-    if (argc == 1) return options;
-    i32 arg_parse_start = 1;
-
-    if (!strcmp(argv[1], "help"))     options.action = ONYX_COMPILE_ACTION_PRINT_HELP;
-    if (!strcmp(argv[1], "compile")) {
-        options.action = ONYX_COMPILE_ACTION_COMPILE;
-        arg_parse_start = 2;
-    }
-    #ifdef ENABLE_RUN_WITH_WASMER
-    else if (!strcmp(argv[1], "run")) {
-        options.action = ONYX_COMPILE_ACTION_RUN;
-        arg_parse_start = 2;
-    }
-    #endif
-    else options.action = ONYX_COMPILE_ACTION_COMPILE;
-
-    if (options.action != ONYX_COMPILE_ACTION_PRINT_HELP) {
-        fori(i, arg_parse_start, argc) {
-            if (!strcmp(argv[i], "-o")) {
-                options.target_file = argv[++i];
-            }
-            else if (!strcmp(argv[i], "--verbose") || !strcmp(argv[i], "-V")) {
-                options.verbose_output = 1;
-            }
-            else if (!strcmp(argv[i], "-VV")) {
-                options.verbose_output = 2;
-            }
-            else if (!strcmp(argv[i], "-VVV")) {
-                options.verbose_output = 3;
-            }
-            else if (!strcmp(argv[i], "--print-function-mappings")) {
-                options.print_function_mappings = 1;
-            }
-            else if (!strcmp(argv[i], "--print-static-if-results")) {
-                options.print_static_if_results = 1;
-            }
-            else if (!strcmp(argv[i], "--print-notes")) {
-                options.print_notes = 1;
-            }
-            else if (!strcmp(argv[i], "--no-colors")) {
-                options.no_colors = 1;
-            }
-            else if (!strcmp(argv[i], "--no-file-contents")) {
-                options.no_file_contents = 1;
-            }
-            else if (!strcmp(argv[i], "--wasm-mvp")) {
-                options.use_post_mvp_features = 0;
-            }
-            else if (!strcmp(argv[i], "--multi-threaded")) {
-                options.use_multi_threading = 1;
-            }
-            else if (!strcmp(argv[i], "--generate-foreign-info")) {
-                options.generate_foreign_info = 1;
-            }
-            else if (!strcmp(argv[i], "-I")) {
-                bh_arr_push(options.included_folders, argv[++i]);
-            }
-            else if (!strcmp(argv[i], "-r") || !strcmp(argv[i], "--runtime")) {
-                i += 1;
-                if      (!strcmp(argv[i], "onyx"))   options.runtime = Runtime_Onyx;
-                else if (!strcmp(argv[i], "wasi"))   options.runtime = Runtime_Wasi;
-                else if (!strcmp(argv[i], "js"))     options.runtime = Runtime_Js;
-                else if (!strcmp(argv[i], "custom")) options.runtime = Runtime_Custom;
-                else {
-                    bh_printf("WARNING: '%s' is not a valid runtime. Defaulting to 'onyx'.\n", argv[i]);
-                    options.runtime = Runtime_Wasi;
-                }
-            }
-            else if (!strcmp(argv[i], "--doc")) {
-                options.documentation_file = argv[++i];
-            }
-            else if (!strcmp(argv[i], "--debug")) {
-                options.debug_enabled = 1;
-            }
-            else if (!strcmp(argv[i], "--")) {
-                options.passthrough_argument_count = argc - i - 1;
-                options.passthrough_argument_data  = &argv[i + 1];
-                break;
-            }
-#if defined(_BH_LINUX)
-            // NOTE: Fun output is only enabled for Linux because Windows command line
-            // is not ANSI compatible and for a silly feature, I don't want to learn
-            // how to properly do arbitrary graphics in it.
-            else if (!strcmp(argv[i], "--fun") || !strcmp(argv[i], "-F")) {
-                options.fun_output = 1;
-            }
-#endif
-            else {
-                bh_arr_push(options.files, argv[i]);
-            }
-        }
-    }
-
-    // NOTE: Always enable multi-threading for the Onyx runtime.
-    if (options.runtime == Runtime_Onyx) {
-        options.use_multi_threading = 1;
-    }
-
-    return options;
-}
-
-static void compile_opts_free(CompileOptions* opts) {
-    bh_arr_free(opts->files);
-    bh_arr_free(opts->included_folders);
-}
-
-
-
-
-
-typedef enum CompilerProgress {
-    ONYX_COMPILER_PROGRESS_ERROR,
-    ONYX_COMPILER_PROGRESS_FAILED_OUTPUT,
-    ONYX_COMPILER_PROGRESS_SUCCESS
-} CompilerProgress;
-
-static OnyxToken implicit_load_token = { '#', 1, 0, { 0, 0, 0, 0, 0 } };
-static AstInclude* create_load(bh_allocator alloc, char* filename) {
-    AstInclude* include_node = onyx_ast_node_new(alloc, sizeof(AstInclude), Ast_Kind_Load_File);
-    include_node->name = filename;
-    include_node->token = &implicit_load_token;
-
-    return include_node;
-}
-
-// HACK
-static u32 special_global_entities_remaining = 3;
-static Entity *runtime_info_types_entity;
-static Entity *runtime_info_foreign_entity;
-static Entity *runtime_info_proc_tags_entity;
-
-static void context_init(CompileOptions* opts) {
-    types_init();
-
-    context.options = opts;
-    context.cycle_detected = 0;
-    context.cycle_almost_detected = 0;
-
-    OnyxFilePos internal_location = { 0 };
-    internal_location.filename = "<compiler internal>";
-    internal_location.line     = 1;
-    internal_location.column   = 1;
-    context.global_scope = scope_create(global_heap_allocator, NULL, internal_location);
-    sh_new_arena(context.packages);
-
-    // NOTE: This will be initialized upon the first call to entity_heap_insert.
-    context.entities.next_id  = 0;
-    context.entities.entities = NULL;
-
-    onyx_errors_init(&context.loaded_files);
-
-    context.token_alloc = global_heap_allocator;
-
-    // NOTE: Create the arena where tokens and AST nodes will exist
-    // Prevents nodes from being scattered across memory due to fragmentation
-    bh_arena_init(&context.ast_arena, global_heap_allocator, 16 * 1024 * 1024); // 16MB
-    context.ast_alloc = bh_arena_allocator(&context.ast_arena);
-
-    context.wasm_module = bh_alloc_item(global_heap_allocator, OnyxWasmModule);
-    *context.wasm_module = onyx_wasm_module_create(global_heap_allocator);
-
-    entity_heap_init(&context.entities);
-
-    // NOTE: Add builtin entities to pipeline.
-    entity_heap_insert(&context.entities, ((Entity) {
-        .state = Entity_State_Parse_Builtin,
-        .type = Entity_Type_Load_File,
-        .package = NULL,
-        .include = create_load(context.ast_alloc, "core/builtin"),
-    }));
-
-    entity_heap_insert(&context.entities, ((Entity) {
-        .state = Entity_State_Parse_Builtin,
-        .type = Entity_Type_Load_File,
-        .package = NULL,
-        .include = create_load(context.ast_alloc, "core/runtime/build_opts"),
-    }));
-
-    if (context.options->runtime != Runtime_Custom) {
-        runtime_info_types_entity = entity_heap_insert(&context.entities, ((Entity) {
-            .state = Entity_State_Parse,
-            .type = Entity_Type_Load_File,
-            .package = NULL,
-            .include = create_load(context.ast_alloc, "core/runtime/info/types"),
-        }));
-        runtime_info_foreign_entity = entity_heap_insert(&context.entities, ((Entity) {
-            .state = Entity_State_Parse,
-            .type = Entity_Type_Load_File,
-            .package = NULL,
-            .include = create_load(context.ast_alloc, "core/runtime/info/foreign_blocks"),
-        }));
-        runtime_info_proc_tags_entity = entity_heap_insert(&context.entities, ((Entity) {
-            .state = Entity_State_Parse,
-            .type = Entity_Type_Load_File,
-            .package = NULL,
-            .include = create_load(context.ast_alloc, "core/runtime/info/proc_tags"),
-        }));
-    }
-
-    add_entities_for_node(NULL, (AstNode *) &builtin_stack_top, context.global_scope, NULL);
-    add_entities_for_node(NULL, (AstNode *) &builtin_heap_start, context.global_scope, NULL);
-    add_entities_for_node(NULL, (AstNode *) &builtin_tls_base, context.global_scope, NULL);
-    add_entities_for_node(NULL, (AstNode *) &builtin_tls_size, context.global_scope, NULL);
-
-    // NOTE: Add all files passed by command line to the queue
-    bh_arr_each(const char *, filename, opts->files) {
-        AstInclude* load_node = create_load(context.ast_alloc, (char *) *filename);
-        add_entities_for_node(NULL, (AstNode *) load_node, context.global_scope, NULL);
-    }
-}
-
-static void context_free() {
-    bh_arena_free(&context.ast_arena);
-    bh_arr_free(context.loaded_files);
-
-    compile_opts_free(context.options);
-}
-
-static void parse_source_file(bh_file_contents* file_contents) {
-    // :Remove passing the allocators as parameters
-    OnyxTokenizer tokenizer = onyx_tokenizer_create(context.token_alloc, file_contents);
-    onyx_lex_tokens(&tokenizer);
-
-    file_contents->line_count = tokenizer.line_number;
-
-    OnyxParser parser = onyx_parser_create(context.ast_alloc, &tokenizer);
-    onyx_parse(&parser);
-    onyx_parser_free(&parser);
-}
-
-static b32 process_source_file(char* filename, OnyxFilePos error_pos) {
-    bh_arr_each(bh_file_contents, fc, context.loaded_files) {
-        // Duplicates are detected here and since these filenames will be the full path,
-        // string comparing them should be all that is necessary.
-        if (!strcmp(fc->filename, filename)) return 1;
-    }
-
-    bh_file file;
-    bh_file_error err = bh_file_open(&file, filename);
-    if (err != BH_FILE_ERROR_NONE) {
-        if (context.cycle_detected) {
-            onyx_report_error(error_pos, Error_Critical, "Failed to open file %s", filename);
-        }
-        return 0;
-    }
-
-    bh_file_contents fc = bh_file_read_contents(context.token_alloc, &file);
-    bh_file_close(&file);
-
-    bh_arr_push(context.loaded_files, fc);
-
-    if (context.options->verbose_output == 2)
-        bh_printf("Processing source file:    %s (%d bytes)\n", file.filename, fc.length);
-
-    parse_source_file(&bh_arr_last(context.loaded_files));
-    return 1;
-}
-
-static b32 process_load_entity(Entity* ent) {
-    assert(ent->type == Entity_Type_Load_File || ent->type == Entity_Type_Load_Path);
-    AstInclude* include = ent->include;
-
-    if (include->kind == Ast_Kind_Load_File) {
-        // :RelativeFiles
-        const char* parent_file = include->token->pos.filename;
-        if (parent_file == NULL) parent_file = ".";
-
-        char* parent_folder = bh_path_get_parent(parent_file, global_scratch_allocator);
-
-        char* filename = bh_lookup_file(include->name, parent_folder, ".onyx", 1, context.options->included_folders, 1);
-        char* formatted_name = bh_strdup(global_heap_allocator, filename);
-
-        return process_source_file(formatted_name, include->token->pos);
-
-    } else if (include->kind == Ast_Kind_Load_All) {
-        const char* parent_file = include->token->pos.filename;
-        if (parent_file == NULL) parent_file = ".";
-
-        char* parent_folder = bh_path_get_parent(parent_file, global_scratch_allocator);
-        char folder[512];
-        if (bh_str_starts_with(include->name, "./")) {
-            bh_snprintf(folder, 511, "%s/%s", parent_folder, include->name + 2);
-        } else {
-            bh_snprintf(folder, 511, "%s", include->name);
-        }
-
-        // This does not take into account #load_path'd folders...
-
-        bh_path_convert_separators(folder);
-        bh_dir dir = bh_dir_open(folder);
-        if (dir == NULL) {
-            onyx_report_error(include->token->pos, Error_Critical, "Could not find folder '%s'.", folder);
-            return 0;
-        }
-
-        bh_dirent entry;
-        b32 success = 1;
-        char fullpath[512];
-        while (bh_dir_read(dir, &entry)) {
-            if (entry.type == BH_DIRENT_FILE && bh_str_ends_with(entry.name, ".onyx")) {
-                bh_snprintf(fullpath, 511, "%s/%s", folder, entry.name);
-                bh_path_convert_separators(fullpath);
-                u8* formatted_name = bh_path_get_full_name(fullpath, global_heap_allocator);
-                success = process_source_file(formatted_name, include->token->pos);
-                if (!success) break;
-            }
-        }
-
-        bh_dir_close(dir);
-        return success;
-
-    } else if (include->kind == Ast_Kind_Load_Path) {
-        bh_arr_push(context.options->included_folders, include->name);
-
-    } else if (include->kind == Ast_Kind_Library_Path) {
-        bh_arr_push(context.wasm_module->library_paths, include->name);
-    }
-
-    return 1;
-}
-
-static b32 process_entity(Entity* ent) {
-    static char verbose_output_buffer[512];
-    if (context.options->verbose_output == 3) {
-        if (ent->expr && ent->expr->token)
-            snprintf(verbose_output_buffer, 511,
-                    "%20s | %24s (%d, %d) | %s:%i:%i \n",
-                   entity_state_strings[ent->state],
-                   entity_type_strings[ent->type],
-                   (u32) ent->macro_attempts,
-                   (u32) ent->micro_attempts,
-                   ent->expr->token->pos.filename,
-                   ent->expr->token->pos.line,
-                   ent->expr->token->pos.column);
-
-        else if (ent->expr)
-            snprintf(verbose_output_buffer, 511,
-                    "%20s | %24s (%d, %d) \n",
-                   entity_state_strings[ent->state],
-                   entity_type_strings[ent->type],
-                   (u32) ent->macro_attempts,
-                   (u32) ent->micro_attempts);
-    }
-
-    // CLEANUP: There should be a nicer way to track if the builtins have
-    // already been initialized.
-    static b32 builtins_initialized = 0;
-
-    EntityState before_state = ent->state;
-    switch (before_state) {
-        case Entity_State_Error:
-            if (ent->type != Entity_Type_Error) {
-                onyx_report_error(ent->expr->token->pos, Error_Critical, "Error entity unexpected. This is definitely a compiler bug");
-            } else {
-                onyx_report_error(ent->error->token->pos, Error_Critical, "Static error occured: '%b'", ent->error->error_msg->text, ent->error->error_msg->length);
-            }
-            break;
-
-        case Entity_State_Parse_Builtin:
-            process_load_entity(ent);
-            ent->state = Entity_State_Finalized;
-            break;
-
-        case Entity_State_Introduce_Symbols:
-            // Currently, introducing symbols is handled in the symbol resolution
-            // function. Maybe there should be a different place where that happens?
-            symres_entity(ent);
-            break;
-
-        case Entity_State_Parse:
-            if (!builtins_initialized) {
-                builtins_initialized = 1;
-                initialize_builtins(context.ast_alloc);
-                introduce_build_options(context.ast_alloc);
-            }
-
-            // GROSS
-            if (special_global_entities_remaining == 0) {
-                special_global_entities_remaining--;
-                initalize_special_globals();
-            }
-
-            if (process_load_entity(ent)) {
-                // GROSS
-                if (ent == runtime_info_types_entity
-                    || ent == runtime_info_proc_tags_entity
-                    || ent == runtime_info_foreign_entity) {
-                    special_global_entities_remaining--;
-                }
-
-                ent->state = Entity_State_Finalized;
-            } else {
-                ent->macro_attempts++;
-            }
-            break;
-
-        case Entity_State_Resolve_Symbols: symres_entity(ent); break;
-        case Entity_State_Check_Types:     check_entity(ent);  break;
-        case Entity_State_Code_Gen:        emit_entity(ent);   break;
-    }
-
-    b32 changed = ent->state != before_state;
-    if (context.options->verbose_output == 3) {
-        if (changed) printf("SUCCESS to %20s | %s", entity_state_strings[ent->state], verbose_output_buffer);
-        else         printf("YIELD   to %20s | %s", entity_state_strings[ent->state], verbose_output_buffer);
-    }
-
-    return changed;
-}
-
-// Just having fun with some visual output - brendanfh 2020/12/14
-#if defined(_BH_LINUX)
-static void output_dummy_progress_bar() {
-    EntityHeap* eh = &context.entities;
-    if (bh_arr_length(eh->entities) == 0) return;
-
-    static const char* state_colors[] = {
-        "\e[91m", "\e[93m", "\e[94m", "\e[93m",
-        "\e[97m", "\e[95m", "\e[96m", "\e[92m", "\e[91m",
-    };
-
-    printf("\e[2;1H");
-
-    for (i32 i = 0; i < Entity_State_Count - 1; i++) {
-        if (i % 2 == 0) printf("\n");
-        printf("%s %25s \xe2\x96\x88 ", state_colors[i], entity_state_strings[i]);
-    }
-
-    printf("\n\n");
-
-    for (i32 i = 0; i < Entity_Type_Count; i++) {
-        if      (eh->type_count[i] == 0)           printf("\e[90m");
-        else if ((i32) eh->entities[0]->type == i) printf("\e[92m");
-        else                                       printf("\e[97m");
-
-        printf("%25s (%4d) | ", entity_type_strings[i], eh->type_count[i]);
-
-        printf("\e[0K");
-        for (i32 j = 0; j < Entity_State_Count; j++) {
-            if (eh->all_count[j][i] == 0) continue;
-
-            printf("%s", state_colors[j]);
-
-            i32 count = (eh->all_count[j][i] >> 5) + 1;
-            for (i32 c = 0; c < count * 2; c++) printf("\xe2\x96\x88");
-
-            printf("\e[0m");
-        }
-        printf("\n");
-    }
-}
-#endif
-
-static void dump_cycles() {
-    context.cycle_detected = 1;
-    Entity* ent;
-
-    while (1) {
-        ent = entity_heap_top(&context.entities);
-        entity_heap_remove_top(&context.entities);
-        if (ent->state < Entity_State_Code_Gen) process_entity(ent);
-        else break;
-
-        if (bh_arr_length(context.entities.entities) == 0) {
-            break;
-        }
-    }
-}
-
-static i32 onyx_compile() {
-    u64 start_time = bh_time_curr();
-
-    if (context.options->fun_output)
-        printf("\e[2J");
-
-    while (!bh_arr_is_empty(context.entities.entities)) {
-        Entity* ent = entity_heap_top(&context.entities);
-
-#if defined(_BH_LINUX)
-        if (context.options->fun_output) {
-            output_dummy_progress_bar();
-
-            if (ent->expr->token) {
-                OnyxFilePos pos = ent->expr->token->pos;
-                printf("\e[0K%s on %s in %s:%d:%d\n", entity_state_strings[ent->state], entity_type_strings[ent->type], pos.filename, pos.line, pos.column);
-            }
-
-            // Slowing things down for the effect
-            usleep(1000);
-        }
-#endif
-
-        entity_heap_remove_top(&context.entities);
-        b32 changed = process_entity(ent);
-
-        // NOTE: VERY VERY dumb cycle breaking. Basically, remember the first entity that did
-        // not change (i.e. did not make any progress). Then everytime an entity doesn't change,
-        // check if it is the same entity. If it is, it means all other entities that were processed
-        // between the two occurences didn't make any progress either, and there must be a cycle.
-        //                                                              - brendanfh 2021/02/06
-        //
-        // Because of the recent changes to the compiler architecture (again), this condition
-        // does not always hold anymore. There can be nodes that get scheduled multiple times
-        // before the "key" node that will unblock the progress. This means a more sophisticated
-        // cycle detection algorithm must be used.
-        //
-        static Entity* watermarked_node = NULL;
-        static u32 highest_watermark = 0;
-        if (!changed) {
-            if (!watermarked_node) {
-                watermarked_node = ent;
-                highest_watermark = bh_max(highest_watermark, ent->macro_attempts);
-            }
-            else if (watermarked_node == ent) {
-                if (ent->macro_attempts > highest_watermark) {
-                    entity_heap_insert_existing(&context.entities, ent);
-
-                    if (context.cycle_almost_detected) {
-                        dump_cycles();
-                    } else {
-                        context.cycle_almost_detected = 1;
-                    }
-                }
-            }
-            else if (watermarked_node->macro_attempts < ent->macro_attempts) {
-                watermarked_node = ent;
-                highest_watermark = bh_max(highest_watermark, ent->macro_attempts);
-            }
-        } else {
-            watermarked_node = NULL;
-            context.cycle_almost_detected = 0;
-        }
-
-        if (onyx_has_errors()) return ONYX_COMPILER_PROGRESS_ERROR;
-
-        if (ent->state != Entity_State_Finalized && ent->state != Entity_State_Failed)
-            entity_heap_insert_existing(&context.entities, ent);
-    }
-
-    u64 duration = bh_time_duration(start_time);
-
-    if (context.options->verbose_output > 0) {
-        // TODO: Replace these with bh_printf when padded formatting is added.
-        printf("\nStatistics:\n");
-        printf("    Time taken: %lf seconds\n", (double) duration / 1000);
-        printf("    Processed %ld lines (%f lines/second).\n", lexer_lines_processed, ((f32) 1000 * lexer_lines_processed) / (duration));
-        printf("    Processed %ld tokens (%f tokens/second).\n", lexer_tokens_processed, ((f32) 1000 * lexer_tokens_processed) / (duration));
-        printf("\n");
-    }
-
-    return ONYX_COMPILER_PROGRESS_SUCCESS;
-}
-
-static void link_wasm_module() {
-    Package *runtime_var_package = package_lookup("runtime.vars");
-    assert(runtime_var_package);
-
-    AstTyped *link_options_node = (AstTyped *) symbol_raw_resolve(runtime_var_package->scope, "link_options");
-    Type *link_options_type = type_build_from_ast(context.ast_alloc, builtin_link_options_type);
-
-    assert(unify_node_and_type(&link_options_node, link_options_type) == TYPE_MATCH_SUCCESS);
-
-    OnyxWasmLinkOptions link_opts;
-    // CLEANUP: Properly handle this case.
-    assert(onyx_wasm_build_link_options_from_node(&link_opts, link_options_node));
-
-    onyx_wasm_module_link(context.wasm_module, &link_opts);
-}
-
-static CompilerProgress onyx_flush_module() {
-    link_wasm_module();
-
-    // NOTE: Output to file
-    bh_file output_file;
-    if (bh_file_create(&output_file, context.options->target_file) != BH_FILE_ERROR_NONE)
-        return ONYX_COMPILER_PROGRESS_FAILED_OUTPUT;
-
-    if (context.options->verbose_output)
-        bh_printf("Outputting to WASM file:   %s\n", output_file.filename);
-
-    // APPARENTLY... the WebAssembly Threading proposal says that the data segment initializations
-    // in a WASM module are copied into the linear memory EVERY time the module is instantiated, not
-    // just the first time. This means that if we are happily chugging along and modifying global state
-    // and then we spawn a thread, that thread will completely wipe all changes to the global and return
-    // it to its original state. This is horrible obviously, but the only thing that is more horrible is
-    // that the best way around this is to create a second WASM module that simply initializes the given
-    // data section. Then have a section module that is actually your code. For right now, this is going
-    // to be fine since the browser is really the only place that multi-threading can be used to any
-    // degree of competency. But still... This is god awful and I hope that there is some other way to
-    // around this down the line.
-    if (context.options->use_multi_threading && !context.options->use_post_mvp_features) {
-        bh_file data_file;
-        if (bh_file_create(&data_file, bh_aprintf(global_scratch_allocator, "%s.data", context.options->target_file)) != BH_FILE_ERROR_NONE)
-            return ONYX_COMPILER_PROGRESS_FAILED_OUTPUT;
-
-        OnyxWasmModule* data_module = bh_alloc_item(global_heap_allocator, OnyxWasmModule);
-        *data_module = onyx_wasm_module_create(global_heap_allocator);
-
-        data_module->data = context.wasm_module->data;
-        context.wasm_module->data = NULL;
-
-        onyx_wasm_module_write_to_file(data_module, data_file);
-        onyx_wasm_module_write_to_file(context.wasm_module, output_file);
-
-        bh_file_close(&data_file);
-    } else {
-        onyx_wasm_module_write_to_file(context.wasm_module, output_file);
-    }
-
-    bh_file_close(&output_file);
-
-    if (context.options->documentation_file != NULL) {
-        OnyxDocumentation docs = onyx_docs_generate();
-        docs.format = Doc_Format_Human;
-        onyx_docs_emit(&docs, context.options->documentation_file);
-    }
-
-    return ONYX_COMPILER_PROGRESS_SUCCESS;
-}
-
-#ifdef ENABLE_RUN_WITH_WASMER
-static b32 onyx_run() {
-    link_wasm_module();
-
-    bh_buffer code_buffer;
-    onyx_wasm_module_write_to_buffer(context.wasm_module, &code_buffer);
-
-    onyx_run_initialize(context.options->debug_enabled);
-
-    if (context.options->verbose_output > 0)
-        bh_printf("Running program:\n");
-
-    return onyx_run_wasm(code_buffer, context.options->passthrough_argument_count, context.options->passthrough_argument_data);
-}
-#endif
-
-int main(int argc, char *argv[]) {
-
-    bh_scratch_init(&global_scratch, bh_heap_allocator(), 256 * 1024); // NOTE: 256 KiB
-    global_scratch_allocator = bh_scratch_allocator(&global_scratch);
-
-    // SPEED: This used to be a managed heap allocator where all allocations
-    // were tracked and would be automatically freed at the end of execution.
-    // I don't know why I ever did that because that is the job of the operating
-    // system when a process exits.
-    global_heap_allocator = bh_heap_allocator();
-
-    CompileOptions compile_opts = compile_opts_parse(global_heap_allocator, argc, argv);
-    context_init(&compile_opts);
-
-    CompilerProgress compiler_progress = ONYX_COMPILER_PROGRESS_ERROR;
-    switch (compile_opts.action) {
-        case ONYX_COMPILE_ACTION_PRINT_HELP: bh_printf(docstring); return 1;
-
-        case ONYX_COMPILE_ACTION_COMPILE:
-            compiler_progress = onyx_compile();
-            if (compiler_progress == ONYX_COMPILER_PROGRESS_SUCCESS) {
-                onyx_flush_module();
-            }
-            break;
-
-        #ifdef ENABLE_RUN_WITH_WASMER
-        case ONYX_COMPILE_ACTION_RUN:
-            compiler_progress = onyx_compile();
-            if (compiler_progress == ONYX_COMPILER_PROGRESS_SUCCESS) {
-                if (!onyx_run()) {
-                    compiler_progress = ONYX_COMPILER_PROGRESS_ERROR;
-                }
-            }
-            break;
-        #endif
-
-        default: break;
-    }
-
-    switch (compiler_progress) {
-        case ONYX_COMPILER_PROGRESS_ERROR:
-            onyx_errors_print();
-            break;
-
-        case ONYX_COMPILER_PROGRESS_FAILED_OUTPUT:
-            bh_printf_err("Failed to open file for writing: '%s'\n", compile_opts.target_file);
-            break;
-
-        case ONYX_COMPILER_PROGRESS_SUCCESS:
-            break;
-    }
-
-    context_free();
-
-    bh_scratch_free(&global_scratch);
-    // bh_managed_heap_free(&global_heap);
-
-    return compiler_progress != ONYX_COMPILER_PROGRESS_SUCCESS;
-}
diff --git a/src/onyx_runtime.c b/src/onyx_runtime.c
deleted file mode 100644 (file)
index 88a1355..0000000
+++ /dev/null
@@ -1,1444 +0,0 @@
-
-#define BH_DEFINE
-#include "bh.h"
-
-#define ONYX_LIBRARY_NAME onyx_runtime
-#define ONYX_NO_SHORT_NAMES
-#include "onyx_library.h"
-
-#ifdef _BH_LINUX
-    #include <pthread.h>
-    #include <signal.h>
-    #include <sys/wait.h>
-    #include <sys/types.h>
-    #include <dlfcn.h>
-    #include <dirent.h>
-    #include <arpa/inet.h>
-    #include <netdb.h>
-    #include <netinet/in.h>
-    #include <sys/socket.h>
-    #include <sys/un.h>
-    #include <poll.h>
-#endif
-
-#include "types.h"  // For POINTER_SIZE
-
-#define ONYX_FILE_ERROR_NONE 0
-#define ONYX_FILE_ERROR_NOT_FOUND 1
-#define ONYX_FILE_ERROR_EXISTS 2
-#define ONYX_FILE_ERROR_PERMISSION 3
-#define ONYX_FILE_ERROR_BAD_FILE 4
-#define ONYX_FILE_ERROR_BAD_MODE 5
-
-ONYX_DEF(__file_open_impl, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
-    char *path_ptr = ONYX_PTR(params->data[0].of.i32);
-    int   path_len = params->data[1].of.i32;
-
-    char path[512] = {0};
-    path_len = bh_min(path_len, 511);
-    strncpy(path, path_ptr, path_len);
-    path[path_len] = 0;
-
-    int mode = params->data[2].of.i32;
-
-    bh_file_mode bh_mode;
-    switch (mode) {
-        case 1: bh_mode = BH_FILE_MODE_READ; break;
-        case 2: bh_mode = BH_FILE_MODE_WRITE; break;
-        case 3: bh_mode = BH_FILE_MODE_APPEND; break;
-    }
-
-    bh_file file;
-    bh_file_error error = bh_file_open_mode(&file, bh_mode, path);
-    if (error == BH_FILE_ERROR_INVALID) {
-        results->data[0] = WASM_I32_VAL(ONYX_FILE_ERROR_NOT_FOUND);
-        return NULL;
-    }
-
-    *(u64 *) ONYX_PTR(params->data[3].of.i32) = (u64) file.fd;
-    results->data[0] = WASM_I32_VAL(ONYX_FILE_ERROR_NONE);
-    return NULL;
-}
-
-ONYX_DEF(__file_close, (WASM_I64), (WASM_I32)) {
-    i64 fd = params->data[0].of.i64;
-
-    bh_file file = { (bh_file_descriptor) fd };
-    bh_file_error error = bh_file_close(&file);
-    if (error == BH_FILE_ERROR_INVALID) {
-        results->data[0] = WASM_I32_VAL(ONYX_FILE_ERROR_NOT_FOUND);
-        return NULL;
-    }
-
-    results->data[0] = WASM_I32_VAL(ONYX_FILE_ERROR_NONE);
-    return NULL;
-}
-
-ONYX_DEF(__file_exists, (WASM_I32, WASM_I32), (WASM_I32)) {
-    char *path_ptr = ONYX_PTR(params->data[0].of.i32);
-    int   path_len = params->data[1].of.i32;
-
-    char path[512] = {0};
-    path_len = bh_min(path_len, 511);
-    strncpy(path, path_ptr, path_len);
-    path[path_len] = 0;
-
-    results->data[0] = WASM_I32_VAL(bh_file_exists(path));
-    return NULL;
-}
-
-ONYX_DEF(__file_stat, (WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
-    char *path_ptr = ONYX_PTR(params->data[0].of.i32);
-    int   path_len = params->data[1].of.i32;
-
-    char path[512] = {0};
-    path_len = bh_min(path_len, 511);
-    strncpy(path, path_ptr, path_len);
-    path[path_len] = 0;
-
-    results->data[0] = WASM_I32_VAL(bh_file_stat(path, ONYX_PTR(params->data[2].of.i32)));
-    return NULL;
-}
-
-ONYX_DEF(__file_remove, (WASM_I32, WASM_I32), (WASM_I32)) {
-    char *path_ptr = ONYX_PTR(params->data[0].of.i32);
-    int   path_len = params->data[1].of.i32;
-
-    char path[512] = {0};
-    path_len = bh_min(path_len, 511);
-    strncpy(path, path_ptr, path_len);
-    path[path_len] = 0;
-
-    results->data[0] = WASM_I32_VAL(bh_file_remove(path));
-    return NULL;
-}
-
-ONYX_DEF(__file_seek, (WASM_I64, WASM_I32, WASM_I32), (WASM_I32)) {
-    i64 fd = params->data[0].of.i64;
-    i32 offset = params->data[1].of.i32;
-    i32 whence = params->data[2].of.i32;
-
-    bh_file file = { (bh_file_descriptor) fd };
-    bh_file_whence bh_whence;
-    switch (whence) {
-        case 0: bh_whence = BH_FILE_WHENCE_BEGIN; break;
-        case 1: bh_whence = BH_FILE_WHENCE_CURRENT; break;
-        case 2: bh_whence = BH_FILE_WHENCE_END; break;
-    }
-
-    i64 new_offset = bh_file_seek(&file, offset, whence);
-    results->data[0] = WASM_I32_VAL((i32) new_offset);
-    return NULL;
-}
-
-ONYX_DEF(__file_tell, (WASM_I64), (WASM_I32)) {
-    i64 fd = params->data[0].of.i64;
-    bh_file file = { (bh_file_descriptor) fd };
-    results->data[0] = WASM_I32_VAL(bh_file_tell(&file));
-    return NULL;
-}
-
-ONYX_DEF(__file_read, (WASM_I64, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
-    i64 fd = params->data[0].of.i64;
-    bh_file file = { (bh_file_descriptor) fd };
-
-    i32 curr_pos = bh_file_tell(&file);
-    b32 success = bh_file_read_at(&file,
-            bh_file_tell(&file),
-            ONYX_PTR(params->data[1].of.i32),
-            params->data[2].of.i32,
-            (i64 *) ONYX_PTR(params->data[3].of.i32));
-
-    bh_file_seek_to(&file, curr_pos + *(i32 *) ONYX_PTR(params->data[3].of.i32));
-
-    results->data[0] = WASM_I32_VAL(0);
-    if (!success) results->data[0] = WASM_I32_VAL(2);
-    return NULL;
-}
-
-ONYX_DEF(__file_write, (WASM_I64, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
-    i64 fd = params->data[0].of.i64;
-    bh_file file = { (bh_file_descriptor) fd };
-
-    i32 curr_pos = bh_file_tell(&file);
-    b32 success = bh_file_write_at(&file,
-            bh_file_tell(&file),
-            ONYX_PTR(params->data[1].of.i32),
-            params->data[2].of.i32,
-            (i64 *) ONYX_PTR(params->data[3].of.i32));
-
-    bh_file_seek_to(&file, curr_pos + *(i32 *) ONYX_PTR(params->data[3].of.i32));
-
-    results->data[0] = WASM_I32_VAL(0);
-    if (!success) results->data[0] = WASM_I32_VAL(2);
-    return NULL;
-}
-
-ONYX_DEF(__file_flush, (WASM_I64), (WASM_I32)) {
-    i64 fd = params->data[0].of.i64;
-    bh_file file = { (bh_file_descriptor) fd };
-    bh_file_flush(&file);
-    results->data[0] = WASM_I32_VAL(0);
-    return NULL;
-}
-
-ONYX_DEF(__file_size, (WASM_I64), (WASM_I32)) {
-    i64 fd = params->data[0].of.i64;
-    bh_file file = { (bh_file_descriptor) fd };
-    results->data[0] = WASM_I32_VAL(bh_file_size(&file));
-    return NULL;
-}
-
-ONYX_DEF(__file_get_standard, (WASM_I32, WASM_I32), (WASM_I32)) {
-    bh_file_standard standard = (bh_file_standard) params->data[0].of.i32;
-
-    bh_file file;
-    bh_file_error error = bh_file_get_standard(&file, standard);
-    if (error == BH_FILE_ERROR_NONE) {
-        *(u64 *) ONYX_PTR(params->data[1].of.i32) = (u64) file.fd;
-    }
-
-    results->data[0] = WASM_I32_VAL(error == BH_FILE_ERROR_NONE);
-    return NULL;
-}
-
-ONYX_DEF(__file_rename, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
-    char *old_path_ptr = ONYX_PTR(params->data[0].of.i32);
-    int   old_path_len = params->data[1].of.i32;
-
-    char old_path[512] = {0};
-    old_path_len = bh_min(old_path_len, 511);
-    strncpy(old_path, old_path_ptr, old_path_len);
-    old_path[old_path_len] = 0;
-
-    char *new_path_ptr = ONYX_PTR(params->data[2].of.i32);
-    int   new_path_len = params->data[3].of.i32;
-
-    char new_path[512] = {0};
-    new_path_len = bh_min(new_path_len, 511);
-    strncpy(new_path, new_path_ptr, new_path_len);
-    new_path[new_path_len] = 0;
-
-#ifdef _BH_WINDOWS
-    results->data[0] = WASM_I32_VAL(MoveFileA(old_path, new_path));
-    return NULL;
-#endif
-
-#ifdef _BH_LINUX
-    results->data[0] = WASM_I32_VAL(rename(old_path, new_path) == 0);
-    return NULL;
-#endif
-}
-
-//
-// Directories
-//
-
-ONYX_DEF(__dir_open, (WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
-    char *path_ptr = ONYX_PTR(params->data[0].of.i32);
-    int   path_len = params->data[1].of.i32;
-
-    char path[512] = {0};
-    path_len = bh_min(path_len, 511);
-    strncpy(path, path_ptr, path_len);
-    path[path_len] = 0;
-
-#ifdef _BH_WINDOWS
-    for (int i=0; i<path_len; i++) if (path[i] == '/') path[i] = '\\';
-    strncat(path, "\\*.*", 511);
-
-    Windows_Directory_Opened* dir = malloc(sizeof(Windows_Directory_Opened));
-    dir->hndl = FindFirstFileA(path, &dir->found_file);
-    if (dir->hndl == INVALID_HANDLE_VALUE) {
-        results->data[0] = WASM_I32_VAL(0);
-        return NULL;
-    }
-
-    *(u64 *) ONYX_PTR(params->data[2].of.i32) = (u64) dir;
-
-    results->data[0] = WASM_I32_VAL(1);
-    return NULL;
-#endif
-
-#ifdef _BH_LINUX
-    DIR* dir = opendir(path);
-    *(u64 *) ONYX_PTR(params->data[2].of.i32) = (u64) dir;
-    results->data[0] = WASM_I32_VAL(dir != NULL);
-    return NULL;
-#endif
-}
-
-// (DIR*, PTR<DIRENT>) -> BOOL
-ONYX_DEF(__dir_read, (WASM_I64, WASM_I32), (WASM_I32)) {
-#ifdef _BH_WINDOWS
-    Windows_Directory_Opened* dir = (Windows_Directory_Opened *) params->data[0].of.i64;
-    if (dir == NULL) {
-        results->data[0] = WASM_I32_VAL(0);
-        return NULL;
-    }
-
-    do {
-        BOOL success = FindNextFileA(dir->hndl, &dir->found_file);
-        if (!success) {
-            results->data[0] = WASM_I32_VAL(0);
-            return NULL;
-        }
-    } while (!strcmp(dir->found_file.cFileName, ".") || !strcmp(dir->found_file.cFileName, ".."));
-
-    u32 out = params->data[1].of.i32;
-    assert(out != 0);
-
-    *(u32 *) ONYX_PTR(out + 0) = (dir->found_file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? 3 : 4;
-    *(u32 *) ONYX_PTR(out + 4) = 0;
-    *(u32 *) ONYX_PTR(out + 8) = strlen(dir->found_file.cFileName);
-    strncpy(ONYX_PTR(out + 12), dir->found_file.cFileName, 256);
-
-    results->data[0] = WASM_I32_VAL(1);
-    return NULL;
-#endif
-
-#ifdef _BH_LINUX
-    DIR* dir = (DIR *) params->data[0].of.i64;
-    if (dir == NULL) {
-        results->data[0] = WASM_I32_VAL(0);
-        return NULL;
-    }
-
-    struct dirent *ent;
-    while (1) {
-        ent = readdir(dir);
-        if (ent == NULL) {
-            results->data[0] = WASM_I32_VAL(0);
-            return NULL;
-        }
-
-        // Skip the current directory and parent directory
-        if (strcmp(ent->d_name, ".") && strcmp(ent->d_name, "..")) break;
-    }
-
-    u32 type = 0;
-    switch (ent->d_type) {
-        case DT_UNKNOWN: break;
-        case DT_BLK: type = 1; break;
-        case DT_CHR: type = 2; break;
-        case DT_DIR: type = 3; break;
-        case DT_LNK: type = 5; break;
-        case DT_REG: type = 4; break;
-        default: type = 6; break;
-    }
-
-    u32 out = params->data[1].of.i32;
-    assert(out != 0);
-
-    *(u32 *) ONYX_PTR(out + 0) = type;
-    *(u32 *) ONYX_PTR(out + 4) = (u32) ent->d_ino;
-    *(u32 *) ONYX_PTR(out + 8) = strlen(ent->d_name);
-    strncpy(ONYX_PTR(out + 12), ent->d_name, 256);
-
-    results->data[0] = WASM_I32_VAL(1);
-    return NULL;
-#endif
-}
-
-ONYX_DEF(__dir_close, (WASM_I64), ()) {
-#ifdef _BH_WINDOWS
-    Windows_Directory_Opened* dir = (Windows_Directory_Opened *) params->data[0].of.i64;
-
-    FindClose(dir->hndl);
-    free(dir);
-
-    return NULL;
-#endif
-
-#ifdef _BH_LINUX
-    DIR* dir = (DIR *) params->data[0].of.i64;
-    if (dir == NULL) return NULL;
-
-    closedir(dir);
-#endif
-    return NULL;
-}
-
-ONYX_DEF(__dir_create, (WASM_I32, WASM_I32), (WASM_I32)) {
-    char *path_ptr = ONYX_PTR(params->data[0].of.i32);
-    int   path_len = params->data[1].of.i32;
-
-    char path[512] = {0};
-    path_len = bh_min(path_len, 511);
-    strncpy(path, path_ptr, path_len);
-    path[path_len] = 0;
-
-#ifdef _BH_WINDOWS
-    results->data[0] = WASM_I32_VAL(CreateDirectoryA(path));
-    return NULL;
-#endif
-
-#ifdef _BH_LINUX
-    results->data[0] = WASM_I32_VAL(mkdir(path, 0777) == 0);
-    return NULL;
-#endif
-}
-
-ONYX_DEF(__dir_remove, (WASM_I32, WASM_I32), (WASM_I32)) {
-    char *path_ptr = ONYX_PTR(params->data[0].of.i32);
-    int   path_len = params->data[1].of.i32;
-
-    char path[512] = {0};
-    path_len = bh_min(path_len, 511);
-    strncpy(path, path_ptr, path_len);
-    path[path_len] = 0;
-
-#ifdef _BH_WINDOWS
-    results->data[0] = WASM_I32_VAL(RemoveDirectoryA(path));
-    return NULL;
-#endif
-
-#ifdef _BH_LINUX
-    results->data[0] = WASM_I32_VAL(rmdir(path) == 0);
-    return NULL;
-#endif
-}
-
-//
-// THREADS
-//
-typedef struct OnyxThread {
-    i32 id;
-    i32 tls_base;
-    i32 funcidx;
-    i32 dataptr;
-    wasm_instance_t* instance;
-
-    #ifdef _BH_LINUX
-        pthread_t thread;
-    #endif
-
-    #ifdef _BH_WINDOWS
-        HANDLE thread_handle;
-        i32    thread_id;
-    #endif
-} OnyxThread;
-
-static bh_arr(OnyxThread) threads = NULL;
-
-#ifdef _BH_LINUX
-static void *onyx_run_thread(void *data) {
-#endif
-#ifdef _BH_WINDOWS
-static i32 onyx_run_thread(void *data) {
-#endif
-    OnyxThread *thread = (OnyxThread *) data;
-
-    wasm_store_t *wasm_store = runtime->wasm_store_new(runtime->wasm_engine);
-
-    wasm_trap_t* traps = NULL;
-    thread->instance = runtime->wasm_instance_new(wasm_store, runtime->wasm_module, &runtime->wasm_imports, &traps);
-
-    wasm_extern_t* start_extern = runtime->wasm_extern_lookup_by_name(runtime->wasm_module, thread->instance, "_thread_start");
-    wasm_func_t*   start_func   = runtime->wasm_extern_as_func(start_extern);
-
-    wasm_extern_t* exit_extern = runtime->wasm_extern_lookup_by_name(runtime->wasm_module, thread->instance, "_thread_exit");
-    wasm_func_t*   exit_func   = runtime->wasm_extern_as_func(exit_extern);
-
-    wasm_trap_t* trap=NULL;
-
-    // NOTE: This is cached in a local variable because there is a tiny chance that if a lot of threads are created
-    // then the backing array for the thread handles will move and thread* we have well not be valid. I'm betting on this
-    // not happening before now in the function; however, it *could* happen before we call thread_exit. This would be bad
-    // because of the normal reasons why accessing memory that you don't own any more is bad.
-    i32 thread_id = thread->id;
-
-    { // Call the _thread_start procedure
-        wasm_val_t args[]    = { WASM_I32_VAL(thread_id), WASM_I32_VAL(thread->tls_base), WASM_I32_VAL(thread->funcidx), WASM_I32_VAL(thread->dataptr) };
-        wasm_val_vec_t results = { 0, 0 };
-        wasm_val_vec_t args_array = WASM_ARRAY_VEC(args);
-
-        trap = runtime->wasm_func_call(start_func, &args_array, &results);
-        if (trap != NULL) {
-            bh_printf("THREAD: %d\n", thread_id);
-            runtime->onyx_print_trap(trap);
-        }
-    }
-
-    { // Call the _thread_exit procedure
-        wasm_val_t args[]    = { WASM_I32_VAL(thread_id) };
-        wasm_val_vec_t results = { 0, 0 };
-        wasm_val_vec_t args_array = WASM_ARRAY_VEC(args);
-
-        trap = runtime->wasm_func_call(exit_func, &args_array, &results);
-    }
-
-    runtime->wasm_store_delete(wasm_store);
-
-    return 0;
-}
-
-ONYX_DEF(__spawn_thread, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
-    if (threads == NULL) bh_arr_new(bh_heap_allocator(), threads, 128);
-    bh_arr_insert_end(threads, 1);
-    OnyxThread *thread = &bh_arr_last(threads);
-
-    thread->id       = params->data[0].of.i32;
-    thread->tls_base = params->data[1].of.i32;
-    thread->funcidx  = params->data[2].of.i32;
-    thread->dataptr  = params->data[3].of.i32;
-
-    #ifdef _BH_LINUX
-        pthread_create(&thread->thread, NULL, onyx_run_thread, thread);
-    #endif
-
-    #ifdef _BH_WINDOWS
-        // thread->thread_handle = CreateThread(NULL, 0, onyx_run_thread, thread, 0, &thread->thread_id);
-        thread->thread_handle = (HANDLE) _beginthreadex(NULL, 0, onyx_run_thread, thread, 0, &thread->thread_id);
-    #endif
-
-    results->data[0] = WASM_I32_VAL(1);
-    return NULL;
-}
-
-ONYX_DEF(__kill_thread, (WASM_I32), (WASM_I32)) {
-    i32 thread_id = params->data[0].of.i32;
-
-    i32 i = 0;
-    bh_arr_each(OnyxThread, thread, threads) {
-        if (thread->id == thread_id) {
-            #ifdef _BH_LINUX
-            // This leads to some weirdness and bugs...
-            //
-            pthread_kill(thread->thread, SIGKILL);
-            #endif
-
-            #ifdef _BH_WINDOWS
-            TerminateThread(thread->thread_handle, 0);
-            CloseHandle(thread->thread_handle);
-            #endif
-
-            bh_arr_deleten(threads, i, 1);
-            results->data[0] = WASM_I32_VAL(1);
-            return NULL;
-        }
-
-        i++;
-    }
-
-    results->data[0] = WASM_I32_VAL(0);
-    return NULL;
-}
-
-
-
-
-//
-// PROCESS
-//
-#define ONYX_PROCESS_MAGIC_NUMBER 0xdeadfadebabecafe
-typedef struct OnyxProcess {
-    u64 magic_number;
-
-#ifdef _BH_LINUX
-    // Pipes
-    i32 proc_to_host[2];
-    i32 host_to_proc[2];
-
-    pid_t pid;
-#endif
-
-#ifdef _BH_WINDOWS
-    HANDLE proc_to_host_read;
-    HANDLE proc_to_host_write;
-    HANDLE host_to_proc_read;
-    HANDLE host_to_proc_write;
-
-    PROCESS_INFORMATION proc_info;
-#endif
-} OnyxProcess;
-
-ONYX_DEF(__process_spawn, (WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I64)) {
-    char* process_str = ONYX_PTR(params->data[0].of.i32);
-    i32   process_len = params->data[1].of.i32;
-    i32   args_ptr    = params->data[2].of.i32;
-    i32   args_len    = params->data[3].of.i32;
-    b32   blocking_io = !params->data[4].of.i32;
-    char *cwd_str     = ONYX_PTR(params->data[5].of.i32);
-    i32   cwd_len     = params->data[6].of.i32;
-
-    char process_path[1024];
-    process_len = bh_min(1023, process_len);
-    memcpy(process_path, process_str, process_len);
-    process_path[process_len] = '\0';
-
-    char starting_dir[1024];
-    if (cwd_len > 0) {
-        cwd_len = bh_min(1023, cwd_len);
-        memcpy(starting_dir, cwd_str, cwd_len);
-        starting_dir[cwd_len] = '\0';
-    }
-
-    OnyxProcess *process = malloc(sizeof(OnyxProcess));
-    memset(process, 0, sizeof(*process));
-    process->magic_number = ONYX_PROCESS_MAGIC_NUMBER;
-
-    #ifdef _BH_LINUX
-        // :Security - alloca a user controlled string.
-        char **process_args = alloca(sizeof(char *) * (args_len + 2));
-        byte_t* array_loc = ONYX_PTR(args_ptr);
-        fori (i, 0, args_len) {
-            char *arg_str = ONYX_PTR(*(i32 *) (array_loc + i * 2 * POINTER_SIZE));
-            i32   arg_len = *(i32 *) (array_loc + i * 2 * POINTER_SIZE + POINTER_SIZE);
-
-            char *arg = alloca(sizeof(char) * (arg_len + 1));
-            memcpy(arg, arg_str, arg_len);
-            arg[arg_len] = '\0';
-            process_args[i + 1] = arg;
-        }
-        process_args[0] = process_path;
-        process_args[args_len + 1] = NULL;
-
-        if (pipe(process->proc_to_host) || pipe(process->host_to_proc)) {
-            wasm_val_init_ptr(&results->data[0], NULL); // Failed to run
-            return NULL;
-        }
-
-        pid_t pid;
-        switch (pid = fork()) {
-            case -1: // Bad fork
-                wasm_val_init_ptr(&results->data[0], NULL); // Failed to run
-
-                close(process->proc_to_host[0]);
-                close(process->proc_to_host[1]);
-                close(process->host_to_proc[0]);
-                close(process->host_to_proc[1]);
-                break;
-
-            case 0: // Child process
-                close(process->proc_to_host[0]);
-                close(process->host_to_proc[1]);
-                dup2(process->host_to_proc[0], 0); // Map the output to the pipe
-                dup2(process->proc_to_host[1], 1); // Map the output to the pipe
-                dup2(process->proc_to_host[1], 2); // Stderr to stdout
-
-                if (!blocking_io) {
-                    fcntl(0, F_SETFL, O_NONBLOCK);
-                    fcntl(1, F_SETFL, O_NONBLOCK);
-                }
-
-                if (cwd_len > 0) {
-                    chdir(starting_dir); // Switch current working directory.
-                }
-
-                execvp(process_path, process_args);
-                exit(-1);
-                break;
-
-            default: {
-                process->pid = pid;
-                close(process->host_to_proc[0]);
-                close(process->proc_to_host[1]);
-
-                wasm_val_init_ptr(&results->data[0], process);
-                break;
-            }
-        }
-
-    #endif
-
-    #ifdef _BH_WINDOWS
-        // CLEANUP CLEANUP CLEANUP: This is so freaking bad...
-        char cmdLine[2048];
-        memset(cmdLine, 0, 2048);
-        strncat(cmdLine, "\"", 2047);
-        strncat(cmdLine, process_path, 2047);
-        strncat(cmdLine, "\"", 2047);
-
-        byte_t* array_loc = ONYX_PTR(args_ptr);
-        fori (i, 0, args_len) {
-            char *arg_str = ONYX_PTR(*(i32 *) (array_loc + i * 2 * POINTER_SIZE));
-            i32   arg_len = *(i32 *) (array_loc + i * 2 * POINTER_SIZE + 4);
-
-            strncat(cmdLine, " \"", 2047);
-            strncat(cmdLine, arg_str, arg_len);
-            strncat(cmdLine, "\"", 2047);
-        }
-
-        STARTUPINFOA startup;
-        memset(&startup, 0, sizeof startup);
-        startup.cb = sizeof(startup);
-
-        SECURITY_ATTRIBUTES saAttr;
-        saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
-        saAttr.lpSecurityDescriptor = NULL;
-        saAttr.bInheritHandle = 1;
-
-        i32 success = 1;
-        success = success && CreatePipe(&process->host_to_proc_read, &process->host_to_proc_write, &saAttr, 4096);
-        success = success && CreatePipe(&process->proc_to_host_read, &process->proc_to_host_write, &saAttr, 4096);
-        if (!success) {
-            // printf("FAILED TO CREATE PIPES: %d\n", GetLastError());
-            wasm_val_init_ptr(&results->data[0], NULL); // Failed to run @LEAK
-            return NULL;
-        }
-
-        success = SetHandleInformation(process->proc_to_host_read, 1 /* HANDLE_FLAG_INHERIT */, 0);
-        success = success && SetHandleInformation(process->host_to_proc_write, 1 /* HANDLE_FLAG_INHERIT */, 0);
-        if (!success) {
-            // printf("FAILED TO CONFIGURE PIPES: %d\n", GetLastError());
-            wasm_val_init_ptr(&results->data[0], NULL); // Failed to run @LEAK
-            return NULL;
-        }
-
-        startup.hStdInput  = process->host_to_proc_read;
-        startup.hStdOutput = process->proc_to_host_write;
-        startup.hStdError = process->proc_to_host_write;
-
-        startup.dwFlags |= STARTF_USESTDHANDLES;
-
-        memset(&process->proc_info, 0, sizeof process->proc_info);
-
-        char *working_dir = NULL;
-        if (cwd_len > 0) {
-            working_dir = starting_dir;
-        }
-
-        success = CreateProcessA(process_path, cmdLine, &saAttr, &saAttr, 1, 0, NULL, working_dir, &startup, &process->proc_info);
-        if (!success) {
-            printf("FAILED TO CREATE PROCESS: %d\n", GetLastError());
-            wasm_val_init_ptr(&results->data[0], NULL); // Failed to run @LEAK
-            return NULL;
-        }
-
-        CloseHandle(process->proc_to_host_write);
-        CloseHandle(process->host_to_proc_read);
-        wasm_val_init_ptr(&results->data[0], process);
-    #endif
-
-    return NULL;
-}
-
-ONYX_DEF(__process_read, (WASM_I64, WASM_I32, WASM_I32), (WASM_I32)) {
-    OnyxProcess *process = (OnyxProcess *) params->data[0].of.i64;
-    if (process == NULL || process->magic_number != ONYX_PROCESS_MAGIC_NUMBER) {
-        results->data[0] = WASM_I32_VAL(0);
-        return NULL;
-    }
-
-    i32 output_ptr = params->data[1].of.i32;
-    i32 output_len = params->data[2].of.i32;
-    u8 *buffer = ONYX_PTR(output_ptr);
-
-    i32 bytes_read;
-    #ifdef _BH_LINUX
-        bytes_read = read(process->proc_to_host[0], buffer, output_len);
-        if (bytes_read < 0) {
-            switch (errno) {
-                case EAGAIN: bytes_read =  0; break;
-                case EBADF:  bytes_read = -1; break;
-                default:     bytes_read = -2; break;
-            }
-        }
-    #endif
-
-    #ifdef _BH_WINDOWS
-        i32 success = ReadFile(process->proc_to_host_read, buffer, output_len, &bytes_read, NULL);
-        if (!success) {
-            bytes_read = -1;
-        }
-    #endif
-
-    results->data[0] = WASM_I32_VAL(bytes_read);
-    return NULL;
-}
-
-ONYX_DEF(__process_write, (WASM_I64, WASM_I32, WASM_I32), (WASM_I32)) {
-    OnyxProcess *process = (OnyxProcess *) params->data[0].of.i64;
-    if (process == NULL || process->magic_number != ONYX_PROCESS_MAGIC_NUMBER) {
-        results->data[0] = WASM_I32_VAL(0);
-        return NULL;
-    }
-
-    i32 input_ptr = params->data[1].of.i32;
-    i32 input_len = params->data[2].of.i32;
-    u8 *buffer = ONYX_PTR(input_ptr);
-
-    i32 bytes_written;
-    #ifdef _BH_LINUX
-        bytes_written = write(process->host_to_proc[1], buffer, input_len);
-        bytes_written = bh_max(bytes_written, 0);  // Silently consume errors
-    #endif
-
-    #ifdef _BH_WINDOWS
-        i32 success = WriteFile(process->host_to_proc_write, buffer, input_len, &bytes_written, NULL);
-        if (!success) bytes_written = 0;
-    #endif
-
-    results->data[0] = WASM_I32_VAL(bytes_written);
-    return NULL;
-}
-
-ONYX_DEF(__process_kill, (WASM_I64), (WASM_I32)) {
-    OnyxProcess *process = (OnyxProcess *) params->data[0].of.i64;
-    if (process == NULL || process->magic_number != ONYX_PROCESS_MAGIC_NUMBER) {
-        results->data[0] = WASM_I32_VAL(0);
-        return NULL;
-    }
-
-    #ifdef _BH_LINUX
-        i32 failed = kill(process->pid, SIGKILL);
-        results->data[0] = WASM_I32_VAL(!failed);
-    #endif
-
-    #ifdef _BH_WINDOWS
-        i32 success = TerminateProcess(process->proc_info.hProcess, 1);
-        results->data[0] = WASM_I32_VAL(success ? 1 : 0);
-    #endif
-
-    return NULL;
-}
-
-ONYX_DEF(__process_wait, (WASM_I64), (WASM_I32)) {
-    OnyxProcess *process = (OnyxProcess *) params->data[0].of.i64;
-    if (process == NULL || process->magic_number != ONYX_PROCESS_MAGIC_NUMBER) {
-        results->data[0] = WASM_I32_VAL(1);
-        return NULL;
-    }
-
-    #ifdef _BH_LINUX
-        i32 status;
-        waitpid(process->pid, &status, 0);
-
-        i32 exit_code = WEXITSTATUS(status);
-        results->data[0] = WASM_I32_VAL(exit_code != 0 ? 2 : 0);
-    #endif
-
-    #ifdef _BH_WINDOWS
-        DWORD exitCode;
-        while (1) {
-            if (!WaitForSingleObject(process->proc_info.hProcess, INFINITE)) {
-                // HACK HACK HACK
-                DWORD error = GetLastError();
-                if (error != 109 && error != 6) {
-                    // printf("ERROR IN WAIT FOR SINGLE: %d\n", error);
-                    results->data[0] = WASM_I32_VAL(1);
-                    return NULL;
-                }
-            }
-
-            if (!GetExitCodeProcess(process->proc_info.hProcess, &exitCode)) {
-                // HACK HACK HACK
-                // Apparently, I'm doing something wrong (maybe?) where the process handle becomes
-                // invalid and causes error 6 "invalid handle". So I think I can safely assume that
-                // if that is the case, then the process exited? probably successfuly? hopefully?
-                // Honestly I don't know and I can't find any documentation describing when a process
-                // handle goes invalid, other than after you close it explicitly. But in the run_tests
-                // script, I'm not calling either process_kill or process_destroy, which are the only
-                // other functions that close the process handle. So I'm left in the dark as to why this
-                // is happening, but oh well. This works for now.
-                //                                                           - brendanfh 2021/12/03
-                if (GetLastError() == 6) {
-                    exitCode = 0;
-                    break;
-                }
-
-                results->data[0] = WASM_I32_VAL(3);
-                return NULL;
-            }
-
-            // 259 is STILL_ACTIVE (aka STATUS_PENDING), which means that the process has not yet exited
-            if (exitCode != 259) break;
-        }
-
-        results->data[0] = WASM_I32_VAL(exitCode != 0 ? 2 : 0);
-    #endif
-
-    return NULL;
-}
-
-ONYX_DEF(__process_destroy, (WASM_I64), ()) {
-    OnyxProcess *process = (OnyxProcess *) params->data[0].of.i64;
-    if (process == NULL || process->magic_number != ONYX_PROCESS_MAGIC_NUMBER) {
-        return NULL;
-    }
-
-    #ifdef _BH_LINUX
-        close(process->proc_to_host[0]);
-        close(process->host_to_proc[1]);
-    #endif
-
-    #ifdef _BH_WINDOWS
-        if (!CloseHandle(process->proc_info.hThread)
-         || !CloseHandle(process->proc_info.hProcess)) {
-            // printf("ERROR CLOSING HANDLES: %d\n", GetLastError());
-         }
-    #endif
-
-    free(process);
-
-    return NULL;
-}
-
-ONYX_DEF(__args_get, (WASM_I32, WASM_I32), ()) {
-    if (runtime->argc == 0 || runtime->argv == NULL) {
-        return NULL;
-    }
-
-    i32 buffer_base = params->data[1].of.i32;
-
-    for (int i=0; i<runtime->argc; i++) {
-        // Should this be strncpy? What would the length be?
-        strcpy(ONYX_PTR(buffer_base), runtime->argv[i]);
-        *(i32 *) ONYX_PTR(params->data[0].of.i32 + i * 4) = buffer_base;
-        buffer_base += strlen(runtime->argv[i]) + 1;
-    }
-
-    return NULL;
-}
-
-ONYX_DEF(__args_sizes_get, (WASM_I32, WASM_I32), ()) {
-    *(i32 *) ONYX_PTR(params->data[0].of.i32) = runtime->argc;
-
-    i32 argv_size = 0;
-    for (int i=0; i<runtime->argc; i++) {
-        argv_size += strlen(runtime->argv[i]) + 1;
-    }
-
-    *(i32 *) ONYX_PTR(params->data[1].of.i32) = argv_size;
-    return NULL;
-}
-
-ONYX_DEF(__exit, (WASM_I32), ()) {
-    exit(params->data[0].of.i32);
-    return NULL;
-}
-
-ONYX_DEF(__sleep, (WASM_I32), ()) {
-    #ifdef _BH_LINUX
-    usleep(params->data[0].of.i32 * 1000);
-    #endif
-
-    #ifdef _BH_WINDOWS
-    Sleep(params->data[0].of.i32);
-    #endif
-    return NULL;
-}
-
-ONYX_DEF(__time, (), (WASM_I64)) {
-    results->data[0] = WASM_I64_VAL(bh_time_curr());
-    return NULL;
-}
-
-
-
-
-//
-// Dates and Times
-//
-ONYX_DEF(__time_localtime, (WASM_I64, WASM_I32), ()) {
-    u64 t = params->data[0].of.i64;
-    *(struct tm *) ONYX_PTR(params->data[1].of.i32) = *localtime(&t);
-    return NULL;
-}
-
-ONYX_DEF(__time_gmtime, (WASM_I64, WASM_I32), ()) {
-    u64 t = params->data[0].of.i64;
-    *(struct tm *) ONYX_PTR(params->data[1].of.i32) = *gmtime(&t);
-    return NULL;
-}
-
-ONYX_DEF(__time_strftime, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
-    u32 len = strftime(ONYX_PTR(params->data[0].of.i32), params->data[1].of.i32, ONYX_PTR(params->data[2].of.i32), ONYX_PTR(params->data[3].of.i32));
-    results->data[0] = WASM_I32_VAL(len); 
-    return NULL;
-}
-
-ONYX_DEF(__time_strptime, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
-    #if defined(_BH_LINUX)
-    char *rem = strptime(ONYX_PTR(params->data[0].of.i32), params->data[1].of.i32, ONYX_PTR(params->data[2].of.i32), ONYX_PTR(params->data[3].of.i32));
-    results->data[0] = WASM_I32_VAL(rem != NULL); 
-    #else
-    results->data[0] = WASM_I32_VAL(0);
-    #endif
-    return NULL;
-}
-
-
-
-//
-// Networking
-//
-struct onyx_socket_addr {
-    unsigned short family;
-    unsigned short port;
-    unsigned int   addr;
-};
-
-static inline int onyx_socket_domain(int i) {
-    #if defined(_BH_LINUX)
-    switch (i) {    // :EnumDependent
-        case 0: return AF_UNIX;
-        case 1: return AF_INET;
-        case 2: return AF_INET6;
-        default: return -1;
-    }
-    #elif defined(_BH_WINDOWS)
-    return -1;
-    #endif
-}
-
-static inline int onyx_socket_protocol(int i) {
-    #if defined(_BH_LINUX)
-    switch (i) {    // :EnumDependent
-        case 0: return SOCK_STREAM;
-        case 1: return SOCK_DGRAM;
-        default: return -1;
-    }
-    #elif defined(_BH_WINDOWS)
-    return -1;
-    #endif
-}
-
-ONYX_DEF(__net_create_socket, (WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
-
-    #ifdef _BH_LINUX
-    int domain = onyx_socket_domain(params->data[1].of.i32);
-    if (domain == -1) goto bad_settings;
-
-    int type = onyx_socket_protocol(params->data[2].of.i32);
-    if (type == -1) goto bad_settings;
-
-    *((int *) ONYX_PTR(params->data[0].of.i32)) = socket(domain, type, 0);
-
-    results->data[0] = WASM_I32_VAL(0);
-    return NULL;
-    #endif 
-
-    #ifdef _BH_WINDOWS
-    #endif
-
-bad_settings:
-    results->data[0] = WASM_I32_VAL(1); // :EnumDependent
-    return NULL;
-}
-
-ONYX_DEF(__net_close_socket, (WASM_I32), ()) {
-    #ifdef _BH_LINUX
-    shutdown(params->data[0].of.i32, SHUT_RDWR);
-    close(params->data[0].of.i32);
-    #endif
-
-    #ifdef _BH_WINDOWS
-    #endif
-
-    return NULL;
-}
-
-ONYX_DEF(__net_setting, (WASM_I32, WASM_I32, WASM_I32), ()) {
-    #ifdef _BH_LINUX
-    switch (params->data[1].of.i32) {
-        case 1: { // :EnumDependent  Non-Blocking
-            int s = params->data[0].of.i32;
-            int flags = fcntl(s, F_GETFL, 0);
-            if (params->data[2].of.i32) {
-                flags |= O_NONBLOCK;
-            } else {
-                flags &= ~O_NONBLOCK;
-            }
-
-            fcntl(s, F_SETFL, flags);
-            break;
-        }
-
-        case 2: { // :EnumDependent  Broadcast
-            int s = params->data[0].of.i32;
-            setsockopt(s, SOL_SOCKET, SO_BROADCAST, (void *) &params->data[2].of.i32, sizeof(int));
-            break;
-        }
-    }
-    #endif
-
-    return NULL;
-}
-
-ONYX_DEF(__net_bind, (WASM_I32, WASM_I32), (WASM_I32)) {
-
-    #ifdef _BH_LINUX
-    int res = -1;
-
-    struct onyx_socket_addr *oaddr = (void *) ONYX_PTR(params->data[1].of.i32);
-    int family = onyx_socket_domain(oaddr->family);
-    int port   = oaddr->port;
-
-    switch (family) {
-        case AF_INET: {
-            struct sockaddr_in bind_addr;
-            memset(&bind_addr, 0, sizeof(bind_addr));
-
-            bind_addr.sin_family = AF_INET;
-            bind_addr.sin_addr.s_addr = htonl(oaddr->addr);
-            bind_addr.sin_port = htons(port);
-
-            res = bind(params->data[0].of.i32, &bind_addr, sizeof(bind_addr));
-            break;
-        }
-
-        case AF_INET6: {
-            struct sockaddr_in6 bind_addr;
-            memset(&bind_addr, 0, sizeof(bind_addr));
-
-            bind_addr.sin6_family = AF_INET6;
-            memcpy(&bind_addr.sin6_addr.s6_addr, (void *) &oaddr->addr, 16);
-            bind_addr.sin6_port = htons(port);
-            
-            res = bind(params->data[0].of.i32, &bind_addr, sizeof(bind_addr));
-            break;
-        }
-
-        case AF_UNIX: {
-            struct sockaddr_un bind_addr;
-            memset(&bind_addr, 0, sizeof(bind_addr));
-
-            bind_addr.sun_family = AF_UNIX;
-            strncpy(&bind_addr.sun_path, (char *) &oaddr->addr, 108);
-            
-            res = bind(params->data[0].of.i32, &bind_addr, sizeof(bind_addr));
-            break;
-        }
-    }
-
-    results->data[0] = WASM_I32_VAL(res >= 0);
-    #endif
-
-    #ifdef _BH_WINDOWS
-    #endif
-
-    return NULL;
-}
-
-ONYX_DEF(__net_listen, (WASM_I32, WASM_I32), ()) {
-    #ifdef _BH_LINUX
-    listen(params->data[0].of.i32, params->data[1].of.i32);
-    #endif
-
-    #ifdef _BH_WINDOWS
-    #endif
-
-    return NULL;
-}
-
-ONYX_DEF(__net_accept, (WASM_I32, WASM_I32), (WASM_I32)) {
-    #ifdef _BH_LINUX
-    struct sockaddr_in client_addr;
-    int client_len = sizeof(client_addr);
-    memset(&client_addr, 0, client_len);
-
-    int client_socket = accept(params->data[0].of.i32, &client_addr, &client_len);
-
-    struct onyx_socket_addr* out_addr = (struct onyx_socket_addr *) ONYX_PTR(params->data[1].of.i32); 
-    out_addr->family = client_addr.sin_family;
-    out_addr->port   = ntohs(client_addr.sin_port);
-    out_addr->addr   = ntohl(client_addr.sin_addr.s_addr);
-
-    results->data[0] = WASM_I32_VAL(client_socket);
-    #endif
-
-    return NULL;
-}
-
-ONYX_DEF(__net_connect_unix, (WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
-    #ifdef _BH_LINUX
-    int   hostlen  = params->data[2].of.i32;
-    char *hostname = alloca(hostlen + 1);
-    memcpy(hostname, ONYX_PTR(params->data[1].of.i32), hostlen);
-    hostname[hostlen] = '\0';
-
-    struct sockaddr_un server_addr;
-    memset(&server_addr, 0, sizeof(server_addr));
-
-    server_addr.sun_family = AF_UNIX; // See comment above
-    memcpy((char *)&server_addr.sun_path, hostname, hostlen);
-
-    int result = connect(params->data[0].of.i32, &server_addr, sizeof(server_addr));
-    if (result == 0) results->data[0] = WASM_I32_VAL(0);
-    else             results->data[0] = WASM_I32_VAL(3); // :EnumDependent
-    #endif
-    
-    return NULL;
-}
-
-ONYX_DEF(__net_connect_ipv4, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
-    #ifdef _BH_LINUX
-    int   hostlen  = params->data[2].of.i32;
-    char *hostname = alloca(hostlen + 1);
-    memcpy(hostname, ONYX_PTR(params->data[1].of.i32), hostlen);
-    hostname[hostlen] = '\0';
-
-    struct hostent *host;
-    host = gethostbyname(hostname); // TODO: Replace this call, as it is obselete.
-    if (host == NULL) {
-        results->data[0] = WASM_I32_VAL(2);  // :EnumDependent
-        return NULL;
-    }
-
-    struct sockaddr_in server_addr;
-    memset(&server_addr, 0, sizeof(server_addr));
-
-    server_addr.sin_family = AF_INET; // See comment above
-    memcpy((char *)&server_addr.sin_addr.s_addr, (char *)host->h_addr, host->h_length);
-    server_addr.sin_port = htons(params->data[3].of.i32);
-
-    int result = connect(params->data[0].of.i32, &server_addr, sizeof(server_addr));
-    if (result == 0) results->data[0] = WASM_I32_VAL(0);
-    else             results->data[0] = WASM_I32_VAL(3); // :EnumDependent
-
-    return NULL;
-    #endif
-
-    #ifdef _BH_WINDOWS
-    #endif
-}
-
-ONYX_DEF(__net_send, (WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
-    #ifdef _BH_LINUX
-    // TODO: The flags at the end should be controllable.
-    int sent = send(params->data[0].of.i32, ONYX_PTR(params->data[1].of.i32), params->data[2].of.i32, MSG_NOSIGNAL);
-    results->data[0] = WASM_I32_VAL(sent);
-    #endif
-    
-    return NULL;
-}
-
-ONYX_DEF(__net_sendto, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
-    #ifdef _BH_LINUX
-    struct sockaddr_in dest_addr;
-    int dest_addr_len = sizeof(dest_addr);
-    memset(&dest_addr, 0, dest_addr_len);
-
-    struct onyx_socket_addr *o_addr = (struct onyx_socket_addr *) ONYX_PTR(params->data[3].of.i32);
-    dest_addr.sin_family = AF_INET; // TODO: See other comments related to AF_NET above.
-    dest_addr.sin_port = htons(o_addr->port);
-    dest_addr.sin_addr.s_addr = htonl(o_addr->addr);
-
-    // TODO: The flags at the end should be controllable.
-    int sent = sendto(params->data[0].of.i32, ONYX_PTR(params->data[1].of.i32), params->data[2].of.i32, MSG_NOSIGNAL, &dest_addr, dest_addr_len);
-    results->data[0] = WASM_I32_VAL(sent);
-    #endif
-    
-    return NULL;
-}
-
-ONYX_DEF(__net_recv, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
-    *(i32 *) ONYX_PTR(params->data[3].of.i32) = 0;
-
-    #ifdef _BH_LINUX
-    // TODO: The flags at the end should be controllable.
-    int received = recv(params->data[0].of.i32, ONYX_PTR(params->data[1].of.i32), params->data[2].of.i32, 0);
-    results->data[0] = WASM_I32_VAL(received);
-
-    if (received < 0) {
-        if (errno == EAGAIN || errno == EWOULDBLOCK) {
-            *(i32 *) ONYX_PTR(params->data[3].of.i32) = 1;
-        }
-    }
-    #endif
-
-    return NULL;
-}
-
-ONYX_DEF(__net_recvfrom, (WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
-    *(i32 *) ONYX_PTR(params->data[4].of.i32) = 0;
-
-    #ifdef _BH_LINUX
-    struct onyx_socket_addr *out_addr = (struct onyx_socket_addr *) ONYX_PTR(params->data[3].of.i32);
-
-    struct sockaddr_in client_addr;
-    int socket_len = sizeof(client_addr);
-    memset(&client_addr, 0, socket_len);
-
-    int received = recvfrom(params->data[0].of.i32, ONYX_PTR(params->data[1].of.i32), params->data[2].of.i32, 0, &client_addr, &socket_len);
-    out_addr->family = client_addr.sin_family;
-    out_addr->port   = ntohs(client_addr.sin_port);
-    out_addr->addr   = ntohl(client_addr.sin_addr.s_addr);
-
-    results->data[0] = WASM_I32_VAL(received);
-
-    if (received < 0) {
-        if (errno == EAGAIN || errno == EWOULDBLOCK) {
-            *(i32 *) ONYX_PTR(params->data[3].of.i32) = 1;
-        }
-    }
-    #endif
-
-    return NULL;
-}
-
-ONYX_DEF(__net_poll_recv, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), ()) {
-    #ifdef _BH_LINUX
-    int i, res, cursor;
-    struct pollfd* fds;
-
-    fds = alloca(params->data[1].of.i32 * sizeof(struct pollfd));
-
-    for (i=0; i<params->data[1].of.i32; i++) {
-        fds[i].fd = *(i32 *) ONYX_PTR(params->data[0].of.i32 + 4 * i);
-        fds[i].events = POLLIN;
-        fds[i].revents = 0;
-    }
-
-    res = poll(fds, params->data[1].of.i32, params->data[2].of.i32);
-
-    for (i=0; i<params->data[1].of.i32; i++) {
-        *(i32 *) ONYX_PTR(params->data[3].of.i32 + 4 * i) = 0; // NO_CHANGE
-
-        if (fds[i].revents & POLLIN) {
-            *(i32 *) ONYX_PTR(params->data[3].of.i32 + 4 * i) = 1; // READABLE
-        }
-
-        if ((fds[i].revents & POLLHUP)
-            || (fds[i].revents & POLLNVAL)
-            || (fds[i].revents & POLLERR)) {
-            *(i32 *) ONYX_PTR(params->data[3].of.i32 + 4 * i) = 2; // CLOSED
-        }
-    }
-
-    #endif
-
-    return NULL;
-}
-
-ONYX_DEF(__net_host_to_net_s, (WASM_I32), (WASM_I32)) {
-    results->data[0] = WASM_I32_VAL(htons(params->data[0].of.i32));
-    return NULL;
-}
-
-ONYX_DEF(__net_host_to_net_l, (WASM_I32), (WASM_I32)) {
-    results->data[0] = WASM_I32_VAL(htonl(params->data[0].of.i32));
-    return NULL;
-}
-
-ONYX_DEF(__net_net_to_host_s, (WASM_I32), (WASM_I32)) {
-    results->data[0] = WASM_I32_VAL(ntohs(params->data[0].of.i32));
-    return NULL;
-}
-
-ONYX_DEF(__net_net_to_host_l, (WASM_I32), (WASM_I32)) {
-    results->data[0] = WASM_I32_VAL(ntohl(params->data[0].of.i32));
-    return NULL;
-}
-
-
-//
-// C-Pointers
-//
-// These are wildly unsafe and break the core principles of the security
-// of WebAssembly, so there should be a way to turn them off!
-//
-ONYX_DEF(__cptr_make, (WASM_I32), (WASM_I64)) {
-    wasm_val_init_ptr(&results->data[0], ONYX_PTR(params->data[0].of.i32));
-    return NULL;
-}
-
-ONYX_DEF(__cptr_read, (WASM_I64, WASM_I32, WASM_I32), ()) {
-    memcpy(ONYX_PTR(params->data[1].of.i32), (void *) params->data[0].of.i64, params->data[2].of.i32);
-    return NULL;
-}
-
-ONYX_DEF(__cptr_read_u8, (WASM_I64), (WASM_I32)) {
-    results->data[0] = WASM_I32_VAL(*(u8 *) params->data[0].of.i64);
-    return NULL;
-}
-
-ONYX_DEF(__cptr_read_u16, (WASM_I64), (WASM_I32)) {
-    results->data[0] = WASM_I32_VAL(*(u16 *) params->data[0].of.i64);
-    return NULL;
-}
-
-ONYX_DEF(__cptr_read_u32, (WASM_I64), (WASM_I32)) {
-    results->data[0] = WASM_I32_VAL(*(u32 *) params->data[0].of.i64);
-    return NULL;
-}
-
-ONYX_DEF(__cptr_read_u64, (WASM_I64), (WASM_I64)) {
-    results->data[0] = WASM_I64_VAL(*(u64 *) params->data[0].of.i64);
-    return NULL;
-}
-
-ONYX_DEF(__cptr_extract_str, (WASM_I64, WASM_I32, WASM_I32), (WASM_I32)) {
-    unsigned int len = strlen((char *) params->data[0].of.i64);
-
-    if (params->data[2].of.i32 != 0) {
-        strncpy(ONYX_PTR(params->data[1].of.i32), (char *) params->data[0].of.i64, params->data[2].of.i32);
-    }
-
-    results->data[0] = WASM_I32_VAL(len);
-    return NULL;
-}
-
-ONYX_LIBRARY {
-    ONYX_FUNC(__file_open_impl)
-    ONYX_FUNC(__file_close)
-    ONYX_FUNC(__file_exists)
-    ONYX_FUNC(__file_stat)
-    ONYX_FUNC(__file_remove)
-    ONYX_FUNC(__file_seek)
-    ONYX_FUNC(__file_tell)
-    ONYX_FUNC(__file_read)
-    ONYX_FUNC(__file_write)
-    ONYX_FUNC(__file_flush)
-    ONYX_FUNC(__file_size)
-    ONYX_FUNC(__file_get_standard)
-    ONYX_FUNC(__file_rename)
-
-    ONYX_FUNC(__dir_open)
-    ONYX_FUNC(__dir_read)
-    ONYX_FUNC(__dir_close)
-    ONYX_FUNC(__dir_create)
-    ONYX_FUNC(__dir_remove)
-
-    ONYX_FUNC(__spawn_thread)
-    ONYX_FUNC(__kill_thread)
-
-    ONYX_FUNC(__process_spawn)
-    ONYX_FUNC(__process_read)
-    ONYX_FUNC(__process_write)
-    ONYX_FUNC(__process_kill)
-    ONYX_FUNC(__process_wait)
-    ONYX_FUNC(__process_destroy)
-
-    ONYX_FUNC(__args_get)
-    ONYX_FUNC(__args_sizes_get)
-
-    ONYX_FUNC(__exit)
-    ONYX_FUNC(__sleep)
-    ONYX_FUNC(__time)
-
-    ONYX_FUNC(__time_localtime)
-    ONYX_FUNC(__time_gmtime)
-    ONYX_FUNC(__time_strftime)
-    ONYX_FUNC(__time_strptime)
-
-    ONYX_FUNC(__net_create_socket)
-    ONYX_FUNC(__net_close_socket)
-    ONYX_FUNC(__net_setting)
-    ONYX_FUNC(__net_bind)
-    ONYX_FUNC(__net_listen)
-    ONYX_FUNC(__net_accept)
-    ONYX_FUNC(__net_connect_unix)
-    ONYX_FUNC(__net_connect_ipv4)
-    ONYX_FUNC(__net_send)
-    ONYX_FUNC(__net_sendto)
-    ONYX_FUNC(__net_recv)
-    ONYX_FUNC(__net_recvfrom)
-    ONYX_FUNC(__net_poll_recv)
-    ONYX_FUNC(__net_host_to_net_s)
-    ONYX_FUNC(__net_host_to_net_l)
-    ONYX_FUNC(__net_net_to_host_s)
-    ONYX_FUNC(__net_net_to_host_l)
-
-    ONYX_FUNC(__cptr_make)
-    ONYX_FUNC(__cptr_read)
-    ONYX_FUNC(__cptr_read_u8)
-    ONYX_FUNC(__cptr_read_u16)
-    ONYX_FUNC(__cptr_read_u32)
-    ONYX_FUNC(__cptr_read_u64)
-    ONYX_FUNC(__cptr_extract_str)
-
-    NULL
-};
diff --git a/src/onyxrun.c b/src/onyxrun.c
deleted file mode 100644 (file)
index 5200090..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-#define BH_DEFINE
-#define BH_NO_TABLE
-#define STB_DS_IMPLEMENTATION
-#include "bh.h"
-
-#include "wasm_emit.h"
-
-int main(int argc, char *argv[]) {
-    i32 wasm_file_idx = 1;
-    if (argc < 2) {
-        fprintf(stderr, "Expected a WASM file to run.\n");
-        return 1;
-    }
-
-    b32 debug = 0;
-    if (!strcmp(argv[1], "--debug")) {
-        debug = 1;
-        wasm_file_idx = 2;
-    }
-
-    if (debug && argc < 3) {
-        fprintf(stderr, "Expected a WASM file to run.\n");
-        return 1;
-    }
-
-    onyx_run_initialize(debug);
-
-    bh_file wasm_file;
-    bh_file_error err = bh_file_open(&wasm_file, argv[wasm_file_idx]);
-    if (err != BH_FILE_ERROR_NONE) {
-        fprintf(stderr, "Failed to open file %s", argv[wasm_file_idx]);
-        return 1;
-    }
-
-    bh_file_contents wasm_data = bh_file_read_contents(bh_heap_allocator(), &wasm_file);
-    bh_file_close(&wasm_file);
-
-    bh_buffer data;
-    data.data = wasm_data.data;
-    data.length = wasm_data.length;
-    return onyx_run_wasm(data, argc - 1 - debug, argv + 1 + debug);
-}
diff --git a/src/parser.c b/src/parser.c
deleted file mode 100644 (file)
index 1ccf752..0000000
+++ /dev/null
@@ -1,3475 +0,0 @@
-// The sole job of the parser for Onyx is to submit nodes to the
-// entity heap for further processing. These nodes include things
-// such as procedure definitions, string literals, struct definitions
-// and declarations to be introduced into scopes.
-
-#include "lex.h"
-#include "errors.h"
-#include "parser.h"
-#include "utils.h"
-
-#define make_node(nclass, kind)             onyx_ast_node_new(parser->allocator, sizeof(nclass), kind)
-// :LinearTokenDependent
-#define peek_token(ahead)                   (parser->curr + ahead)
-
-static AstNode error_node = { Ast_Kind_Error, 0, NULL, NULL };
-
-#define ENTITY_SUBMIT(node)                 (submit_entity_in_scope(parser, (AstNode *) (node), parser->current_scope, parser->package))
-#define ENTITY_SUBMIT_IN_SCOPE(node, scope) (submit_entity_in_scope(parser, (AstNode *) (node), scope, parser->package))
-
-void submit_entity_in_scope(OnyxParser* parser, AstNode* node, Scope* scope, Package* package) {
-    if (bh_arr_length(parser->alternate_entity_placement_stack) == 0) {
-        add_entities_for_node(NULL, node, scope, package);
-
-    } else {
-        bh_arr(Entity *) *entity_array = bh_arr_last(parser->alternate_entity_placement_stack);
-        add_entities_for_node(entity_array, node, scope, package);
-    }
-}
-
-// Parsing Utilities
-static void consume_token(OnyxParser* parser);
-static OnyxToken* expect_token(OnyxParser* parser, TokenType token_type);
-static b32 consume_token_if_next(OnyxParser* parser, TokenType token_type);
-static b32 next_tokens_are(OnyxParser* parser, i32 n, ...);
-static OnyxToken* find_matching_paren(OnyxToken* paren);
-
-static AstNumLit*     parse_int_literal(OnyxParser* parser);
-static AstNumLit*     parse_float_literal(OnyxParser* parser);
-static b32            parse_possible_struct_literal(OnyxParser* parser, AstTyped* left, AstTyped** ret);
-static b32            parse_possible_array_literal(OnyxParser* parser, AstTyped* left, AstTyped** ret);
-static b32            parse_possible_unary_field_access(OnyxParser* parser, AstTyped** ret);
-static void           parse_arguments(OnyxParser* parser, TokenType end_token, Arguments* args);
-static AstTyped*      parse_factor(OnyxParser* parser);
-static AstTyped*      parse_compound_assignment(OnyxParser* parser, AstTyped* lhs);
-static AstTyped*      parse_compound_expression(OnyxParser* parser, b32 assignment_allowed);
-static AstTyped*      parse_expression(OnyxParser* parser, b32 assignment_allowed);
-static AstIfWhile*    parse_if_stmt(OnyxParser* parser);
-static AstIfWhile*    parse_while_stmt(OnyxParser* parser);
-static AstFor*        parse_for_stmt(OnyxParser* parser);
-static AstSwitchCase* parse_case_stmt(OnyxParser* parser);
-static AstSwitch*     parse_switch_stmt(OnyxParser* parser);
-static i32            parse_possible_symbol_declaration(OnyxParser* parser, AstNode** ret);
-static AstReturn*     parse_return_stmt(OnyxParser* parser);
-static AstNode*       parse_use_stmt(OnyxParser* parser);
-static AstBlock*      parse_block(OnyxParser* parser, b32 make_a_new_scope, char* block_name);
-static AstNode*       parse_statement(OnyxParser* parser);
-static void           parse_polymorphic_variable(OnyxParser* parser, AstType*** next_insertion);
-static AstType*       parse_type(OnyxParser* parser);
-static AstTypeOf*     parse_typeof(OnyxParser* parser);
-static AstStructType* parse_struct(OnyxParser* parser);
-static AstInterface*  parse_interface(OnyxParser* parser);
-static AstConstraint* parse_constraint(OnyxParser* parser);
-static void           parse_constraints(OnyxParser* parser, ConstraintContext *constraints);
-static void           parse_function_params(OnyxParser* parser, AstFunction* func);
-static b32            parse_possible_directive(OnyxParser* parser, const char* dir);
-static b32            parse_possible_function_definition_no_consume(OnyxParser* parser);
-static b32            parse_possible_function_definition(OnyxParser* parser, AstTyped** ret);
-static b32            parse_possible_quick_function_definition_no_consume(OnyxParser* parser);
-static b32            parse_possible_quick_function_definition(OnyxParser* parser, AstTyped** ret);
-static AstFunction*   parse_function_definition(OnyxParser* parser, OnyxToken* token);
-static AstTyped*      parse_global_declaration(OnyxParser* parser);
-static AstEnumType*   parse_enum_declaration(OnyxParser* parser);
-static AstMacro*      parse_macro(OnyxParser* parser);
-static AstIf*         parse_static_if_stmt(OnyxParser* parser, b32 parse_block_as_statements);
-static AstMemRes*     parse_memory_reservation(OnyxParser* parser, OnyxToken* symbol, b32 thread_local);
-static AstTyped*      parse_top_level_expression(OnyxParser* parser);
-static AstBinding*    parse_top_level_binding(OnyxParser* parser, OnyxToken* symbol);
-static void           parse_top_level_statement(OnyxParser* parser);
-static AstPackage*    parse_package_expression(OnyxParser* parser);
-static void           parse_top_level_statements_until(OnyxParser* parser, TokenType tt);
-
-static void consume_token(OnyxParser* parser) {
-    if (parser->hit_unexpected_token) return;
-
-    parser->prev = parser->curr;
-    // :LinearTokenDependent
-    parser->curr++;
-    while (parser->curr->type == Token_Type_Comment || parser->curr->type == Token_Type_Note) {
-        if (parser->curr->type == Token_Type_Note) {
-            AstNote* note = make_node(AstNode, Ast_Kind_Note);
-            note->token = parser->curr;
-            ENTITY_SUBMIT(note);
-        }
-
-        parser->curr++;
-    }
-}
-
-static OnyxToken* find_matching_paren(OnyxToken* paren) {
-    TokenType match = 0;
-    switch ((u16) paren->type) {
-        case '(': match = ')'; break;
-        case '[': match = ']'; break;
-        case '{': match = '}'; break;
-        case '<': match = '>'; break;
-        default: return NULL;
-    }
-
-    i32 paren_count = 1;
-    i32 i = 1;
-    while (paren_count > 0) {
-        // :LinearTokenDependent
-        TokenType type = (paren + i)->type;
-        if (type == Token_Type_End_Stream) return NULL;
-
-        if (type == paren->type) paren_count++;
-        else if (type == match)  paren_count--;
-
-        i++;
-    }
-
-    // :LinearTokenDependent
-    return paren + (i - 1);
-}
-
-// NOTE: This advances to next token no matter what
-static OnyxToken* expect_token(OnyxParser* parser, TokenType token_type) {
-    if (parser->hit_unexpected_token) return NULL;
-
-    OnyxToken* token = parser->curr;
-    consume_token(parser);
-
-    if (token->type != token_type) {
-        onyx_report_error(token->pos, Error_Critical, "expected token '%s', got '%s'.", token_name(token_type), token_name(token->type));
-        parser->hit_unexpected_token = 1;
-        // :LinearTokenDependent
-        parser->curr = &parser->tokenizer->tokens[bh_arr_length(parser->tokenizer->tokens) - 1];
-        return NULL;
-    }
-
-    return token;
-}
-
-static b32 consume_token_if_next(OnyxParser* parser, TokenType token_type) {
-    if (parser->hit_unexpected_token) return 0;
-
-    if (parser->curr->type == token_type) {
-        consume_token(parser);
-        return 1;
-    } else {
-        return 0;
-    }
-}
-
-static void consume_tokens(OnyxParser* parser, i32 n) {
-    fori (i, 0, n) consume_token(parser);
-}
-
-static b32 next_tokens_are(OnyxParser* parser, i32 n, ...) {
-    va_list va;
-    va_start(va, n);
-
-    i32 matched = 1;
-
-    // BUG: This does not take into consideration comments and notes that can occur between any tokens.
-    fori (i, 0, n) {
-        TokenType expected_type = va_arg(va, TokenType);
-        if (peek_token(i)->type != expected_type) {
-            matched = 0;
-            break;
-        }
-    }
-
-    va_end(va);
-    return matched;
-}
-
-
-static void expect_no_stored_tags_pos(OnyxParser *parser, OnyxFilePos pos) {
-    if (bh_arr_length(parser->stored_tags) > 0) {
-        onyx_report_error(pos, Error_Critical, "#tag is not allowed on this element.");
-        parser->hit_unexpected_token = 1;
-    }
-}
-
-static void expect_no_stored_tags(OnyxParser *parser) {
-    expect_no_stored_tags_pos(parser, parser->curr->pos);
-}
-
-static void flush_stored_tags(OnyxParser *parser, bh_arr(AstTyped *) *out_arr) {
-    //
-    // When tag_depth > 0, no tags will be added to the element.
-    // This happens if you have a something like so,
-    //
-    // #tag "asdf"
-    // #tag handler.{ (x: i32) => x * 2 }
-    // foo :: () { ... }
-    //
-    // In this situation, the inner procedure defined in the second
-    // tag should NOT consume the "asdf" tag.
-    //
-    if (bh_arr_length(parser->stored_tags) == 0 || parser->tag_depth > 0) return;
-
-    bh_arr(AstTyped *) arr = *out_arr;
-
-    if (arr == NULL) {
-        bh_arr_new(parser->allocator, arr, bh_arr_length(parser->stored_tags));
-    }
-
-    bh_arr_each(AstTyped *, pexpr, parser->stored_tags) {
-        bh_arr_push(arr, *pexpr);
-    }
-
-    bh_arr_clear(parser->stored_tags);
-
-    *out_arr = arr;
-}
-
-
-// TODO: Make parsing numeric literals not rely on the C standard libary.
-static AstNumLit* parse_int_literal(OnyxParser* parser) {
-    AstNumLit* int_node = make_node(AstNumLit, Ast_Kind_NumLit);
-    int_node->token = expect_token(parser, Token_Type_Literal_Integer);
-    int_node->flags |= Ast_Flag_Comptime;
-    int_node->value.l = 0ll;
-
-    token_toggle_end(int_node->token);
-
-    char* first_invalid = NULL;
-    i64 value = strtoll(int_node->token->text, &first_invalid, 0);
-
-    int_node->value.l = value;
-
-    int_node->type_node = (AstType *) &basic_type_int_unsized;
-
-    // NOTE: Hex literals are unsigned.
-    if (int_node->token->length >= 2 && int_node->token->text[1] == 'x') {
-        int_node->was_hex_literal = 1;
-    }
-
-    token_toggle_end(int_node->token);
-    return int_node;
-}
-
-static AstNumLit* parse_float_literal(OnyxParser* parser) {
-    AstNumLit* float_node = make_node(AstNumLit, Ast_Kind_NumLit);
-    float_node->token = expect_token(parser, Token_Type_Literal_Float);
-    float_node->flags |= Ast_Flag_Comptime;
-    float_node->value.d = 0.0;
-
-    AstType* type = (AstType *) &basic_type_float_unsized;
-    token_toggle_end(float_node->token);
-
-    if (float_node->token->text[float_node->token->length - 1] == 'f') {
-        type = (AstType *) &basic_type_f32;
-        float_node->value.f = strtof(float_node->token->text, NULL);
-    } else {
-        float_node->value.d = strtod(float_node->token->text, NULL);
-    }
-
-    float_node->type_node = type;
-
-    token_toggle_end(float_node->token);
-    return float_node;
-}
-
-static b32 parse_possible_directive(OnyxParser* parser, const char* dir) {
-    if (!next_tokens_are(parser, 2, '#', Token_Type_Symbol)) return 0;
-
-    OnyxToken* sym = peek_token(1);
-
-    b32 match = (strlen(dir) == (u64) sym->length) && (strncmp(dir, sym->text, sym->length) == 0);
-    if (match) consume_tokens(parser, 2);
-
-    return match;
-}
-
-static b32 parse_possible_struct_literal(OnyxParser* parser, AstTyped* left, AstTyped** ret) {
-    if (!next_tokens_are(parser, 2, '.', '{')) return 0;
-
-    AstStructLiteral* sl = make_node(AstStructLiteral, Ast_Kind_Struct_Literal);
-    sl->token = parser->curr;
-    sl->stnode = left;
-
-    arguments_initialize(&sl->args);
-
-    expect_token(parser, '.');
-    expect_token(parser, '{');
-
-    parse_arguments(parser, '}', &sl->args);
-
-    *ret = (AstTyped *) sl;
-    return 1;
-}
-
-static b32 parse_possible_array_literal(OnyxParser* parser, AstTyped* left, AstTyped** ret) {
-    if (!next_tokens_are(parser, 2, '.', '[')) return 0;
-
-    AstArrayLiteral* al = make_node(AstArrayLiteral, Ast_Kind_Array_Literal);
-    al->token = parser->curr;
-    al->atnode = left;
-
-    bh_arr_new(global_heap_allocator, al->values, 4);
-    fori (i, 0, 4) al->values[i] = NULL;
-
-    expect_token(parser, '.');
-    expect_token(parser, '[');
-    while (!consume_token_if_next(parser, ']')) {
-        if (parser->hit_unexpected_token) return 1;
-
-        AstTyped* value = parse_expression(parser, 0);
-        bh_arr_push(al->values, value);
-
-        if (parser->curr->type != ']')
-            expect_token(parser, ',');
-    }
-
-    *ret = (AstTyped *) al;
-    return 1;
-}
-
-static b32 parse_possible_unary_field_access(OnyxParser* parser, AstTyped** ret) {
-    if (!next_tokens_are(parser, 2, '.', Token_Type_Symbol)) return 0;
-
-    AstUnaryFieldAccess* ufl = make_node(AstUnaryFieldAccess, Ast_Kind_Unary_Field_Access);
-    expect_token(parser, '.');
-    ufl->token = expect_token(parser, Token_Type_Symbol);
-
-    *ret = (AstTyped *) ufl;
-    return 1;
-}
-
-static void parse_arguments(OnyxParser* parser, TokenType end_token, Arguments* args) {
-    while (!consume_token_if_next(parser, end_token)) {
-        if (parser->hit_unexpected_token) return;
-
-        //
-        // This has a weird condition to avoid the problem of using a quick function as an argument:
-        //     f(x => x + 1)
-        // This shouldn't be a named argument, but this should:
-        //     f(g = x => x + 1)
-        //
-        if (next_tokens_are(parser, 2, Token_Type_Symbol, '=') && peek_token(2)->type != '>') {
-            OnyxToken* name = expect_token(parser, Token_Type_Symbol);
-            expect_token(parser, '=');
-
-            AstNamedValue* named_value = make_node(AstNamedValue, Ast_Kind_Named_Value);
-            named_value->token = name;
-            named_value->value = parse_expression(parser, 0);
-
-            bh_arr_push(args->named_values, named_value);
-
-        } else {
-            AstTyped* value = parse_expression(parser, 0);
-            bh_arr_push(args->values, value);
-        }
-
-        if (parser->curr->type != end_token)
-            expect_token(parser, ',');
-    }
-}
-
-static AstTyped* parse_factor(OnyxParser* parser) {
-    AstTyped* retval = NULL;
-
-    switch ((u16) parser->curr->type) {
-        case '(': {
-            if (parse_possible_function_definition(parser, &retval)) {
-                ENTITY_SUBMIT(retval);
-                break;
-            }
-            if (parse_possible_quick_function_definition(parser, &retval)) {
-                ENTITY_SUBMIT(retval);
-                break;
-            }
-
-            consume_token(parser);
-            retval = parse_compound_expression(parser, 0);
-            expect_token(parser, ')');
-            break;
-        }
-
-        case '-': {
-            AstUnaryOp* negate_node = make_node(AstUnaryOp, Ast_Kind_Unary_Op);
-            negate_node->operation = Unary_Op_Negate;
-            negate_node->token     = expect_token(parser, '-');
-            negate_node->expr      = parse_factor(parser);
-
-            retval = (AstTyped *) negate_node;
-            break;
-        }
-
-        case '!': {
-            AstUnaryOp* not_node = make_node(AstUnaryOp, Ast_Kind_Unary_Op);
-            not_node->operation = Unary_Op_Not;
-            not_node->token = expect_token(parser, '!');
-            not_node->expr = parse_factor(parser);
-
-            retval = (AstTyped *) not_node;
-            break;
-        }
-
-        case '~': {
-            AstUnaryOp* not_node = make_node(AstUnaryOp, Ast_Kind_Unary_Op);
-            not_node->operation = Unary_Op_Bitwise_Not;
-            not_node->token = expect_token(parser, '~');
-            not_node->expr = parse_factor(parser);
-
-            retval = (AstTyped *) not_node;
-            break;
-        }
-
-        case '*': {
-            AstDereference* deref_node = make_node(AstDereference, Ast_Kind_Dereference);
-            deref_node->token = expect_token(parser, '*');
-            deref_node->expr  = parse_factor(parser);
-
-            retval = (AstTyped *) deref_node;
-            break;
-        }
-
-        case '^': {
-            AstAddressOf* aof_node = make_node(AstAddressOf, Ast_Kind_Address_Of);
-            aof_node->token = expect_token(parser, '^');
-            aof_node->expr  = parse_factor(parser);
-
-            retval = (AstTyped *) aof_node;
-            break;
-        }
-
-        case '.': {
-            if (parse_possible_struct_literal(parser, NULL, &retval)) return retval;
-            if (parse_possible_array_literal(parser, NULL, &retval))  return retval;
-            if (parse_possible_unary_field_access(parser, &retval))   return retval;
-            goto no_match;
-        }
-
-        case Token_Type_Tilde_Tilde: {
-            AstUnaryOp* ac_node = make_node(AstUnaryOp, Ast_Kind_Unary_Op);
-            ac_node->operation = Unary_Op_Auto_Cast;
-            ac_node->token = expect_token(parser, Token_Type_Tilde_Tilde);
-            ac_node->expr = parse_factor(parser);
-
-            retval = (AstTyped *) ac_node;
-            break;
-        }
-
-        case Token_Type_Keyword_Cast: {
-            AstUnaryOp* cast_node = make_node(AstUnaryOp, Ast_Kind_Unary_Op);
-            cast_node->operation = Unary_Op_Cast;
-            cast_node->token = expect_token(parser, Token_Type_Keyword_Cast);
-
-            expect_token(parser, '(');
-            cast_node->type_node = parse_type(parser);
-            expect_token(parser, ')');
-
-            cast_node->expr = parse_factor(parser);
-
-            retval = (AstTyped *) cast_node;
-            break;
-        }
-
-        case Token_Type_Keyword_Sizeof: {
-            AstSizeOf* so_node = make_node(AstSizeOf, Ast_Kind_Size_Of);
-            so_node->token = expect_token(parser, Token_Type_Keyword_Sizeof);
-            so_node->so_ast_type = (AstType *) parse_type(parser);
-            so_node->type_node = (AstType *) &basic_type_i32;
-
-            retval = (AstTyped *) so_node;
-            break;
-        }
-
-        case Token_Type_Keyword_Alignof: {
-            AstAlignOf* ao_node = make_node(AstAlignOf, Ast_Kind_Align_Of);
-            ao_node->token = expect_token(parser, Token_Type_Keyword_Alignof);
-            ao_node->ao_ast_type = (AstType *) parse_type(parser);
-            ao_node->type_node = (AstType *) &basic_type_i32;
-
-            retval = (AstTyped *) ao_node;
-            break;
-        }
-
-        case Token_Type_Keyword_Typeof: {
-            retval = (AstTyped *) parse_typeof(parser);
-            break;
-        }
-
-        case Token_Type_Symbol: {
-            if (parse_possible_quick_function_definition(parser, &retval)) {
-                ENTITY_SUBMIT(retval);
-                break;
-            }
-
-            OnyxToken* sym_token = expect_token(parser, Token_Type_Symbol);
-            AstTyped* sym_node = make_node(AstTyped, Ast_Kind_Symbol);
-            sym_node->token = sym_token;
-
-            retval = sym_node;
-            break;
-        }
-
-        case Token_Type_Literal_Integer:
-            retval = (AstTyped *) parse_int_literal(parser);
-            break;
-
-        case Token_Type_Literal_Float:
-            retval = (AstTyped *) parse_float_literal(parser);
-            break;
-
-        case Token_Type_Literal_String: {
-            AstStrLit* str_node = make_node(AstStrLit, Ast_Kind_StrLit);
-            str_node->token     = expect_token(parser, Token_Type_Literal_String);
-            str_node->data_id   = 0;
-            str_node->flags    |= Ast_Flag_Comptime;
-
-            ENTITY_SUBMIT(str_node);
-
-            retval = (AstTyped *) str_node;
-            break;
-        }
-
-        case Token_Type_Literal_True: {
-            AstNumLit* bool_node = make_node(AstNumLit, Ast_Kind_NumLit);
-            bool_node->type_node = (AstType *) &basic_type_bool;
-            bool_node->token = expect_token(parser, Token_Type_Literal_True);
-            bool_node->value.i = 1;
-            bool_node->flags |= Ast_Flag_Comptime;
-            retval = (AstTyped *) bool_node;
-            break;
-        }
-
-        case Token_Type_Literal_False: {
-            AstNumLit* bool_node = make_node(AstNumLit, Ast_Kind_NumLit);
-            bool_node->type_node = (AstType *) &basic_type_bool;
-            bool_node->token = expect_token(parser, Token_Type_Literal_False);
-            bool_node->value.i = 0;
-            bool_node->flags |= Ast_Flag_Comptime;
-            retval = (AstTyped *) bool_node;
-            break;
-        }
-
-        case Token_Type_Keyword_Package: {
-            retval = (AstTyped *) parse_package_expression(parser);
-            break;
-        }
-
-        case Token_Type_Keyword_Macro: {
-            retval = (AstTyped *) parse_macro(parser);
-            break;
-        }
-
-        case Token_Type_Keyword_Do: {
-            OnyxToken* do_token = expect_token(parser, Token_Type_Keyword_Do);
-            AstDoBlock* do_block = make_node(AstDoBlock, Ast_Kind_Do_Block);
-            do_block->token = do_token;
-            do_block->type_node = (AstType *) &basic_type_auto_return;
-
-            if (parser->curr->type != '{') {
-                onyx_report_error(parser->curr->pos, Error_Critical, "Expected '{' after 'do', got '%s'.", token_name(parser->curr->type));
-                retval = NULL;
-                break;
-            }
-
-            do_block->block = parse_block(parser, 1, NULL);
-
-            retval = (AstTyped *) do_block;
-            break;
-        }
-
-        case '[': {
-            AstType *type = parse_type(parser);
-            retval = (AstTyped *) type;
-            break;
-        }
-
-        case '$': {
-            AstType **tmp = (AstType **) &retval;
-            parse_polymorphic_variable(parser, &tmp);
-            break;
-        }
-
-        case '#': {
-            if (parse_possible_directive(parser, "file_contents")) {
-                AstFileContents* fc = make_node(AstFileContents, Ast_Kind_File_Contents);
-                fc->token = parser->prev - 1;
-                fc->filename_expr = parse_expression(parser, 0);
-                fc->type = type_make_slice(parser->allocator, &basic_types[Basic_Kind_U8]);
-
-                if (parser->current_function_stack && bh_arr_length(parser->current_function_stack) > 0) {
-                    bh_arr_push(bh_arr_last(parser->current_function_stack)->nodes_that_need_entities_after_clone, (AstNode *) fc);
-                    
-                } else {
-                    ENTITY_SUBMIT(fc);
-                }
-
-                retval = (AstTyped *) fc;
-                break;
-            }
-            else if (parse_possible_directive(parser, "file")) {
-                OnyxToken* dir_token = parser->curr - 2;
-
-                OnyxToken* str_token = bh_alloc(parser->allocator, sizeof(OnyxToken));
-                str_token->text  = bh_strdup(global_heap_allocator, (char *) dir_token->pos.filename);
-                str_token->length = strlen(dir_token->pos.filename);
-                str_token->pos = dir_token->pos;
-                str_token->type = Token_Type_Literal_String;
-
-                AstStrLit* filename = make_node(AstStrLit, Ast_Kind_StrLit);
-                filename->token = str_token;
-                filename->data_id = 0;
-
-                ENTITY_SUBMIT(filename);
-                retval = (AstTyped *) filename;
-                break;
-            }
-            else if (parse_possible_directive(parser, "line")) {
-                OnyxToken* dir_token = parser->curr - 2;
-
-                AstNumLit* line_num = make_int_literal(parser->allocator, dir_token->pos.line);
-                retval = (AstTyped *) line_num;
-                break;
-            }
-            else if (parse_possible_directive(parser, "column")) {
-                OnyxToken* dir_token = parser->curr - 2;
-
-                AstNumLit* col_num = make_int_literal(parser->allocator, dir_token->pos.column);
-                retval = (AstTyped *) col_num;
-                break;
-            }
-            else if (parse_possible_directive(parser, "char")) {
-                AstNumLit* char_lit = make_node(AstNumLit, Ast_Kind_NumLit);
-                char_lit->flags |= Ast_Flag_Comptime;
-                char_lit->type_node = (AstType *) &basic_type_int_unsized;
-                char_lit->token = expect_token(parser, Token_Type_Literal_String);
-
-                i8 dest = '\0';
-                i32 length = string_process_escape_seqs((char *) &dest, char_lit->token->text, 1);
-                char_lit->value.i = (u32) dest;
-
-                if (length != 1) {
-                    onyx_report_error(char_lit->token->pos, Error_Critical, "Expected only a single character in character literal.");
-                }
-
-                retval = (AstTyped *) char_lit;
-                break;
-            }
-            else if (parse_possible_directive(parser, "type")) {
-                AstTypeAlias* alias = make_node(AstTypeAlias, Ast_Kind_Type_Alias);
-                alias->token = parser->curr - 2;
-                alias->to = parse_type(parser);
-                retval = (AstTyped *) alias;
-                break;
-            }
-            else if (parse_possible_directive(parser, "solidify")) {
-                AstDirectiveSolidify* solid = make_node(AstDirectiveSolidify, Ast_Kind_Directive_Solidify);
-                // :LinearTokenDependent
-                solid->token = parser->curr - 1;
-
-                solid->poly_proc = (AstFunction *) parse_factor(parser);
-
-                solid->known_polyvars = NULL;
-                bh_arr_new(global_heap_allocator, solid->known_polyvars, 2);
-
-                expect_token(parser, '{');
-                while (!consume_token_if_next(parser, '}')) {
-                    if (parser->hit_unexpected_token) break;
-
-                    AstNode* poly_var = make_node(AstNode, Ast_Kind_Symbol);
-                    poly_var->token = expect_token(parser, Token_Type_Symbol);
-
-                    expect_token(parser, '=');
-                    AstType* poly_type = (AstType *) parse_expression(parser, 0);
-
-                    bh_arr_push(solid->known_polyvars, ((AstPolySolution) {
-                        .kind     = PSK_Undefined,
-                        .poly_sym = poly_var,
-                        .ast_type = poly_type,
-                        .type     = NULL
-                    }));
-
-                    if (parser->curr->type != '}')
-                        expect_token(parser, ',');
-                }
-
-                retval = (AstTyped *) solid;
-                break;
-            }
-            else if (parse_possible_directive(parser, "defined")) {
-                AstDirectiveDefined* defined = make_node(AstDirectiveDefined, Ast_Kind_Directive_Defined);
-                // :LinearTokenDependent
-                defined->token = parser->curr - 1;
-                defined->type_node = (AstType *) &basic_type_bool;
-
-                expect_token(parser, '(');
-                defined->expr = parse_expression(parser, 0);
-                expect_token(parser, ')');
-
-                retval = (AstTyped *) defined;
-                break;
-            }
-            else if (parse_possible_directive(parser, "quote")) {
-                OnyxToken* code_token = parser->curr - 1;
-
-                AstCodeBlock* code_block = make_node(AstCodeBlock, Ast_Kind_Code_Block);
-                code_block->token = code_token;
-
-                assert(builtin_code_type != NULL);
-                code_block->type_node = builtin_code_type;
-
-                if (parser->curr->type == '{') {
-                    code_block->code = (AstNode *) parse_block(parser, 1, NULL);
-                    ((AstBlock *) code_block->code)->rules = Block_Rule_Code_Block;
-
-                } else {
-                    code_block->code = (AstNode *) parse_expression(parser, 0);
-                }
-
-                retval = (AstTyped *) code_block;
-                break;
-            }
-            else if (next_tokens_are(parser, 2, '#', '(')) {
-                OnyxToken* code_token = expect_token(parser, '#');
-                expect_token(parser, '(');
-
-                AstCodeBlock* code_block = make_node(AstCodeBlock, Ast_Kind_Code_Block);
-                code_block->token = code_token;
-
-                assert(builtin_code_type != NULL);
-                code_block->type_node = builtin_code_type;
-
-                code_block->code = (AstNode *) parse_expression(parser, 0);
-
-                expect_token(parser, ')');
-
-                retval = (AstTyped *) code_block;
-                break;
-            }
-            else if (parse_possible_directive(parser, "unquote")) {
-                AstDirectiveInsert* insert = make_node(AstDirectiveInsert, Ast_Kind_Directive_Insert);
-                insert->token = parser->curr - 1;
-                insert->code_expr = parse_expression(parser, 0);
-
-                retval = (AstTyped *) insert;
-                break;
-            }
-            else if (parse_possible_directive(parser, "cstr")) {
-                // Copy pasted from above.
-                AstStrLit* str_node = make_node(AstStrLit, Ast_Kind_StrLit);
-                str_node->token     = expect_token(parser, Token_Type_Literal_String);
-                str_node->data_id   = 0;
-                str_node->flags    |= Ast_Flag_Comptime;
-                str_node->is_cstr   = 1;
-
-                ENTITY_SUBMIT(str_node);
-
-                retval = (AstTyped *) str_node;
-                break;
-            }
-
-            onyx_report_error(parser->curr->pos, Error_Critical, "Invalid directive in expression.");
-            return NULL;
-        }
-
-        default:
-        no_match:
-            onyx_report_error(parser->curr->pos, Error_Critical, "Unexpected token '%s'.", token_name(parser->curr->type));
-            return NULL;
-    }
-
-    while (1) {
-        if (parser->hit_unexpected_token) return retval;
-
-        switch ((u16) parser->curr->type) {
-            case '[': {
-                OnyxToken *open_bracket = expect_token(parser, '[');
-                AstTyped *expr = parse_compound_expression(parser, 0);
-
-                AstSubscript *sub_node = make_node(AstSubscript, Ast_Kind_Subscript);
-                sub_node->token = open_bracket;
-                sub_node->addr = retval;
-                sub_node->expr = expr;
-                sub_node->__unused_operation = Binary_Op_Subscript;
-
-                retval = (AstTyped *) sub_node;
-                expect_token(parser, ']');
-                break;
-            }
-
-            case '.': {
-                if (parse_possible_struct_literal(parser, retval, &retval)) return retval;
-                if (parse_possible_array_literal(parser, retval, &retval))  return retval;
-
-                consume_token(parser);
-                AstFieldAccess* field = make_node(AstFieldAccess, Ast_Kind_Field_Access);
-                field->token = expect_token(parser, Token_Type_Symbol);
-                field->expr  = retval;
-
-                retval = (AstTyped *) field;
-                break;
-            }
-
-            case '(': {
-                if (!parser->parse_calls) goto factor_parsed;
-
-                AstCall* call_node = make_node(AstCall, Ast_Kind_Call);
-                call_node->token = expect_token(parser, '(');
-                call_node->callee = retval;
-
-                arguments_initialize(&call_node->args);
-
-                parse_arguments(parser, ')', &call_node->args);
-
-                // Wrap expressions in AstArgument
-                bh_arr_each(AstTyped *, arg, call_node->args.values) {
-                    if ((*arg) == NULL) continue;
-                    *arg = (AstTyped *) make_argument(parser->allocator, *arg);
-                }
-
-                bh_arr_each(AstNamedValue *, named_value, call_node->args.named_values) {
-                    if ((*named_value)->value == NULL) continue;
-                    (*named_value)->value = (AstTyped *) make_argument(parser->allocator, (AstTyped *) (*named_value)->value);
-                }
-
-                retval = (AstTyped *) call_node;
-                break;
-            }
-
-            case Token_Type_Keyword_If: {
-                AstIfExpression* if_expression = make_node(AstIfExpression, Ast_Kind_If_Expression);
-                if_expression->token = expect_token(parser, Token_Type_Keyword_If);
-
-                if_expression->true_expr  = retval;
-                if_expression->cond       = parse_expression(parser, 0);
-                expect_token(parser, Token_Type_Keyword_Else);
-                if_expression->false_expr = parse_expression(parser, 0);
-
-                retval = (AstTyped *) if_expression;
-                break;
-            }
-
-            default: goto factor_parsed;
-        }
-    }
-
-factor_parsed:
-
-    return retval;
-}
-
-static inline i32 get_precedence(BinaryOp kind) {
-    switch (kind) {
-        case Binary_Op_Assign:          return 1;
-        case Binary_Op_Assign_Add:      return 1;
-        case Binary_Op_Assign_Minus:    return 1;
-        case Binary_Op_Assign_Multiply: return 1;
-        case Binary_Op_Assign_Divide:   return 1;
-        case Binary_Op_Assign_Modulus:  return 1;
-        case Binary_Op_Assign_And:      return 1;
-        case Binary_Op_Assign_Or:       return 1;
-        case Binary_Op_Assign_Xor:      return 1;
-        case Binary_Op_Assign_Shl:      return 1;
-        case Binary_Op_Assign_Shr:      return 1;
-        case Binary_Op_Assign_Sar:      return 1;
-
-        case Binary_Op_Pipe:            return 2;
-        case Binary_Op_Range:           return 2;
-
-        case Binary_Op_Bool_And:        return 3;
-        case Binary_Op_Bool_Or:         return 3;
-
-        case Binary_Op_Equal:           return 4;
-        case Binary_Op_Not_Equal:       return 4;
-
-        case Binary_Op_Less_Equal:      return 5;
-        case Binary_Op_Less:            return 5;
-        case Binary_Op_Greater_Equal:   return 5;
-        case Binary_Op_Greater:         return 5;
-
-        case Binary_Op_And:             return 6;
-        case Binary_Op_Or:              return 6;
-        case Binary_Op_Xor:             return 6;
-        case Binary_Op_Shl:             return 6;
-        case Binary_Op_Shr:             return 6;
-        case Binary_Op_Sar:             return 6;
-
-        case Binary_Op_Add:             return 7;
-        case Binary_Op_Minus:           return 7;
-
-        case Binary_Op_Multiply:        return 8;
-        case Binary_Op_Divide:          return 8;
-
-        case Binary_Op_Modulus:         return 9;
-        
-        case Binary_Op_Method_Call:     return 10;
-
-        default:                        return -1;
-    }
-}
-
-static BinaryOp binary_op_from_token_type(TokenType t) {
-    switch ((u16) t) {
-        case Token_Type_Equal_Equal:       return Binary_Op_Equal;
-        case Token_Type_Not_Equal:         return Binary_Op_Not_Equal;
-        case Token_Type_Less_Equal:        return Binary_Op_Less_Equal;
-        case Token_Type_Greater_Equal:     return Binary_Op_Greater_Equal;
-        case '<':                          return Binary_Op_Less;
-        case '>':                          return Binary_Op_Greater;
-
-        case '+':                          return Binary_Op_Add;
-        case '-':                          return Binary_Op_Minus;
-        case '*':                          return Binary_Op_Multiply;
-        case '/':                          return Binary_Op_Divide;
-        case '%':                          return Binary_Op_Modulus;
-
-        case '&':                          return Binary_Op_And;
-        case '|':                          return Binary_Op_Or;
-        case '^':                          return Binary_Op_Xor;
-        case Token_Type_Shift_Left:        return Binary_Op_Shl;
-        case Token_Type_Shift_Right:       return Binary_Op_Shr;
-        case Token_Type_Shift_Arith_Right: return Binary_Op_Sar;
-
-        case Token_Type_And_And:           return Binary_Op_Bool_And;
-        case Token_Type_Or_Or:             return Binary_Op_Bool_Or;
-
-        case '=':                          return Binary_Op_Assign;
-        case Token_Type_Plus_Equal:        return Binary_Op_Assign_Add;
-        case Token_Type_Minus_Equal:       return Binary_Op_Assign_Minus;
-        case Token_Type_Star_Equal:        return Binary_Op_Assign_Multiply;
-        case Token_Type_Fslash_Equal:      return Binary_Op_Assign_Divide;
-        case Token_Type_Percent_Equal:     return Binary_Op_Assign_Modulus;
-        case Token_Type_And_Equal:         return Binary_Op_Assign_And;
-        case Token_Type_Or_Equal:          return Binary_Op_Assign_Or;
-        case Token_Type_Xor_Equal:         return Binary_Op_Assign_Xor;
-        case Token_Type_Shl_Equal:         return Binary_Op_Assign_Shl;
-        case Token_Type_Shr_Equal:         return Binary_Op_Assign_Shr;
-        case Token_Type_Sar_Equal:         return Binary_Op_Assign_Sar;
-
-        case Token_Type_Pipe:              return Binary_Op_Pipe;
-        case Token_Type_Dot_Dot:           return Binary_Op_Range;
-        case '[':                          return Binary_Op_Subscript;
-        case Token_Type_Right_Arrow:       return Binary_Op_Method_Call;
-        default: return Binary_Op_Count;
-    }
-}
-
-static AstTyped* parse_compound_assignment(OnyxParser* parser, AstTyped* lhs) {
-    if (parser->curr->type != '=') return lhs;
-
-    AstBinaryOp* assignment = make_node(AstBinaryOp, Ast_Kind_Binary_Op);
-    assignment->token = expect_token(parser, '=');
-    assignment->operation = Binary_Op_Assign;
-    assignment->left = lhs;
-    assignment->right = parse_compound_expression(parser, 0);
-
-    return (AstTyped *) assignment;
-}
-
-static AstTyped* parse_compound_expression(OnyxParser* parser, b32 assignment_allowed) {
-    AstTyped* first = parse_expression(parser, assignment_allowed);
-
-    if (parser->curr->type == ',') {
-        AstCompound* compound = make_node(AstCompound, Ast_Kind_Compound);
-        compound->token = parser->curr;
-
-        bh_arr_new(global_heap_allocator, compound->exprs, 2);
-        bh_arr_push(compound->exprs, first);
-
-        while (consume_token_if_next(parser, ',')) {
-            if (parser->hit_unexpected_token) return (AstTyped *) compound;
-
-            AstTyped* expr = parse_expression(parser, 0);
-            bh_arr_push(compound->exprs, expr);
-
-            if (assignment_allowed && parser->curr->type == '=') {
-                return parse_compound_assignment(parser, (AstTyped *) compound);
-            }
-        }
-
-        return (AstTyped *) compound;
-
-    } else {
-        return first;
-    }
-}
-
-static AstTyped* parse_expression(OnyxParser* parser, b32 assignment_allowed) {
-    bh_arr(AstBinaryOp*) tree_stack = NULL;
-    bh_arr_new(global_heap_allocator, tree_stack, 4);
-    bh_arr_set_length(tree_stack, 0);
-
-    AstTyped* left = parse_factor(parser);
-    AstTyped* right;
-    AstTyped* root = left;
-
-    BinaryOp bin_op_kind;
-    OnyxToken* bin_op_tok;
-
-    while (1) {
-        if (parser->hit_unexpected_token) return root;
-
-        bin_op_kind = binary_op_from_token_type(parser->curr->type);
-        if (bin_op_kind == Binary_Op_Count) goto expression_done;
-        if (binop_is_assignment(bin_op_kind) && !assignment_allowed) goto expression_done;
-        if (bin_op_kind == Binary_Op_Subscript) goto expression_done;
-
-        bin_op_tok = parser->curr;
-        consume_token(parser);
-
-        AstBinaryOp* bin_op;
-        if      (bin_op_kind == Binary_Op_Pipe)        bin_op = make_node(AstBinaryOp, Ast_Kind_Pipe);
-        else if (bin_op_kind == Binary_Op_Method_Call) bin_op = make_node(AstBinaryOp, Ast_Kind_Method_Call);
-        else if (bin_op_kind == Binary_Op_Range)       bin_op = (AstBinaryOp *) make_node(AstRangeLiteral, Ast_Kind_Range_Literal);
-        else                                           bin_op = make_node(AstBinaryOp, Ast_Kind_Binary_Op);
-
-        bin_op->token = bin_op_tok;
-        bin_op->operation = bin_op_kind;
-
-        while ( !bh_arr_is_empty(tree_stack) &&
-                get_precedence(bh_arr_last(tree_stack)->operation) >= get_precedence(bin_op_kind))
-            bh_arr_pop(tree_stack);
-
-        if (bh_arr_is_empty(tree_stack)) {
-            // NOTE: new is now the root node
-            bin_op->left = root;
-            root = (AstTyped *) bin_op;
-
-        } else {
-            bin_op->left = bh_arr_last(tree_stack)->right;
-            bh_arr_last(tree_stack)->right = (AstTyped *) bin_op;
-        }
-
-        bh_arr_push(tree_stack, bin_op);
-
-        right = parse_factor(parser);
-        bin_op->right = right;
-    }
-
-expression_done:
-    bh_arr_free(tree_stack);
-    return root;
-}
-
-static AstIfWhile* parse_if_stmt(OnyxParser* parser) {
-    AstIfWhile* if_node = make_node(AstIfWhile, Ast_Kind_If);
-    if_node->token = expect_token(parser, Token_Type_Keyword_If);
-
-    AstIfWhile* root_if = if_node;
-    AstTyped* cond;
-    AstNode* initialization_or_cond=NULL;
-    b32 had_initialization = 0;
-    if (parse_possible_symbol_declaration(parser, &initialization_or_cond)) {
-        had_initialization = 1;
-
-    } else {
-        // NOTE: Assignment is allowed here because instead of not parsing correctly,
-        // an error is reported in the typechecking, saying that assignment isn't allowed
-        // here, which is better than an unexpected token error.
-        initialization_or_cond = (AstNode *) parse_compound_expression(parser, 1);
-    }
-
-    if (had_initialization || parser->curr->type == ';') {
-        expect_token(parser, ';');
-        cond = parse_expression(parser, 1);
-    } else {
-        cond = (AstTyped *) initialization_or_cond;
-        initialization_or_cond = NULL;
-    }
-
-    AstBlock* true_stmt = parse_block(parser, 1, NULL);
-
-    if_node->initialization = initialization_or_cond;
-    if_node->cond = cond;
-    if (true_stmt != NULL)
-        if_node->true_stmt = true_stmt;
-
-    while (consume_token_if_next(parser, Token_Type_Keyword_Elseif)) {
-        if (parser->hit_unexpected_token) return root_if;
-
-        AstIfWhile* elseif_node = make_node(AstIfWhile, Ast_Kind_If);
-        elseif_node->token = parser->curr - 1;
-
-        cond = parse_expression(parser, 1);
-        true_stmt = parse_block(parser, 1, NULL);
-
-        elseif_node->cond = cond;
-        if (true_stmt != NULL)
-            elseif_node->true_stmt = true_stmt;
-
-        if_node->false_stmt = (AstBlock *) elseif_node;
-        if_node = elseif_node;
-    }
-
-    if (consume_token_if_next(parser, Token_Type_Keyword_Else)) {
-        AstBlock* false_stmt = parse_block(parser, 1, NULL);
-        if (false_stmt != NULL)
-            if_node->false_stmt = false_stmt;
-    }
-
-    return root_if;
-}
-
-static AstIfWhile* parse_while_stmt(OnyxParser* parser) {
-    OnyxToken* while_token = expect_token(parser, Token_Type_Keyword_While);
-    AstIfWhile* while_node = make_node(AstIfWhile, Ast_Kind_While);
-    while_node->token = while_token;
-
-    if (parse_possible_directive(parser, "bottom_test")) {
-        while_node->bottom_test = 1;
-    }
-
-    AstTyped* cond;
-    AstNode* initialization_or_cond=NULL;
-    b32 had_initialization = 0;
-    if (parse_possible_symbol_declaration(parser, &initialization_or_cond)) {
-        had_initialization = 1;
-
-    } else {
-        // NOTE: Assignment is allowed here because instead of not parsing correctly,
-        // an error is reported in the typechecking, saying that assignment isn't allowed
-        // here, which is better than an unexpected token error.
-        initialization_or_cond = (AstNode *) parse_compound_expression(parser, 1);
-    }
-
-    if (had_initialization || parser->curr->type == ';') {
-        expect_token(parser, ';');
-        cond = parse_expression(parser, 1);
-    } else {
-        cond = (AstTyped *) initialization_or_cond;
-        initialization_or_cond = NULL;
-    }
-
-    while_node->initialization = initialization_or_cond;
-    while_node->cond = cond;
-    while_node->true_stmt = parse_block(parser, 1, NULL);
-
-    if (consume_token_if_next(parser, Token_Type_Keyword_Else)) {
-        while_node->false_stmt = parse_block(parser, 1, NULL);
-    }
-
-    return while_node;
-}
-
-static AstFor* parse_for_stmt(OnyxParser* parser) {
-    AstFor* for_node = make_node(AstFor, Ast_Kind_For);
-    for_node->token = expect_token(parser, Token_Type_Keyword_For);
-
-    if (parse_possible_directive(parser, "no_close")) {
-        for_node->no_close = 1;
-    }
-
-    if (consume_token_if_next(parser, '^')) {
-        for_node->by_pointer = 1;
-    }
-
-    if (next_tokens_are(parser, 2, Token_Type_Symbol, ':')) {
-        OnyxToken* local_sym = expect_token(parser, Token_Type_Symbol);
-        AstLocal* var_node = make_local(parser->allocator, local_sym, NULL);
-
-        for_node->var = var_node;
-
-        expect_token(parser, ':');
-    } else {
-        // HACK
-        static char it_name[] = "it ";
-        static OnyxToken it_token = { Token_Type_Symbol, 2, it_name, { 0 } };
-
-        AstLocal* var_node = make_local(parser->allocator, &it_token, NULL);
-        for_node->var = var_node;
-    }
-
-    for_node->iter = parse_expression(parser, 1);
-    for_node->stmt = parse_block(parser, 1, NULL);
-
-    return for_node;
-}
-
-static AstSwitchCase* parse_case_stmt(OnyxParser* parser) {
-    AstSwitchCase *sc_node = make_node(AstSwitchCase, Ast_Kind_Switch_Case);
-    sc_node->token = expect_token(parser, Token_Type_Keyword_Case);
-
-    if (parse_possible_directive(parser, "default")) {
-        sc_node->is_default = 1;
-
-    } else {
-        bh_arr_new(global_heap_allocator, sc_node->values, 1);
-
-        AstTyped* value = parse_expression(parser, 1);
-        bh_arr_push(sc_node->values, value);
-        while (consume_token_if_next(parser, ',')) {
-            if (parser->hit_unexpected_token) return sc_node;
-
-            value = parse_expression(parser, 1);
-            bh_arr_push(sc_node->values, value);
-        }
-    }
-
-    sc_node->block = parse_block(parser, 1, NULL);
-
-    return sc_node;
-}
-
-static AstSwitch* parse_switch_stmt(OnyxParser* parser) {
-    AstSwitch* switch_node = make_node(AstSwitch, Ast_Kind_Switch);
-    switch_node->token = expect_token(parser, Token_Type_Keyword_Switch);
-
-    AstTyped* expr;
-    AstNode* initialization_or_expr=NULL;
-    b32 had_initialization = 0;
-    if (parse_possible_symbol_declaration(parser, &initialization_or_expr)) {
-        had_initialization = 1;
-
-    } else {
-        // NOTE: Assignment is allowed here because instead of not parsing correctly,
-        // an error is reported in the typechecking, saying that assignment isn't allowed
-        // here, which is better than an unexpected token error.
-        initialization_or_expr = (AstNode *) parse_compound_expression(parser, 1);
-    }
-
-    if (had_initialization || parser->curr->type == ';') {
-        expect_token(parser, ';');
-        expr = parse_expression(parser, 1);
-
-    } else {
-        expr = (AstTyped *) initialization_or_expr;
-        initialization_or_expr = NULL;
-    }
-
-    switch_node->initialization = initialization_or_expr;
-    switch_node->expr = expr;
-
-    switch_node->case_block = parse_block(parser, 1, NULL);
-    return switch_node;
-}
-
-static i32 parse_possible_compound_symbol_declaration(OnyxParser* parser, AstNode** ret) {
-    u32 token_offset = 0;
-    while (peek_token(token_offset)->type == Token_Type_Symbol) {
-        token_offset += 1;
-
-        if (peek_token(token_offset)->type == '\'') token_offset += 1;
-
-        if (peek_token(token_offset)->type != ',') break;
-        token_offset += 1;
-    }
-
-    if (peek_token(token_offset)->type != ':') return 0;
-
-    // At this point, we are sure it is a compound declaration.
-    AstCompound* local_compound = make_node(AstCompound, Ast_Kind_Compound);
-    bh_arr_new(global_heap_allocator, local_compound->exprs, token_offset / 2);
-
-    AstLocal* first_local = NULL;
-    AstLocal* prev_local  = NULL;
-
-    while (parser->curr->type == Token_Type_Symbol) {
-        if (parser->hit_unexpected_token) return 1;
-
-        OnyxToken* local_sym = expect_token(parser, Token_Type_Symbol);
-        AstNode* sym_node = make_symbol(parser->allocator, local_sym);
-        bh_arr_push(local_compound->exprs, (AstTyped *) sym_node);
-
-        if (!consume_token_if_next(parser, '\'')) {
-            AstLocal* new_local = make_local(parser->allocator, local_sym, NULL);
-            if (prev_local == NULL) {
-                first_local = new_local;
-            } else {
-                prev_local->next = (AstNode *) new_local;
-            }
-            prev_local = new_local;
-        }
-
-        consume_token_if_next(parser, ',');
-    }
-
-    expect_token(parser, ':');
-
-    if (parser->curr->type == '=') {
-        AstBinaryOp* assignment = make_binary_op(parser->allocator, Binary_Op_Assign, (AstTyped *) local_compound, NULL);
-        assignment->token = expect_token(parser, '=');
-        assignment->right = parse_compound_expression(parser, 0);
-
-        prev_local->next = (AstNode *) assignment;
-
-    } else {
-        AstType* type_for_all = parse_type(parser);
-        forll (AstLocal, local, first_local, next) {
-            local->type_node = type_for_all;
-        }
-    }
-
-    *ret = (AstNode *) first_local;
-    return 1;
-}
-
-// Returns:
-//     0 - if this was not a symbol declaration.
-//     1 - if this was a local declaration.
-//     2 - if this was binding declaration.
-// ret is set to the statement to insert
-static i32 parse_possible_symbol_declaration(OnyxParser* parser, AstNode** ret) {
-    // Has to start with a symbol to be a declaration
-    if (parser->curr->type != Token_Type_Symbol) return 0;
-
-    // If the token after the symbol is a comma, assume this is a compound declaration.
-    if (peek_token(1)->type == ',' ||
-        (peek_token(1)->type == '\'' && peek_token(2)->type == ',')) {
-        return parse_possible_compound_symbol_declaration(parser, ret);
-    }
-
-    if (peek_token(1)->type != ':') return 0;
-
-    OnyxToken* symbol = expect_token(parser, Token_Type_Symbol);
-    expect_token(parser, ':');
-
-    if (parser->curr->type == ':') {
-        bh_arr_push(parser->current_symbol_stack, symbol);
-        AstBinding* binding = parse_top_level_binding(parser, symbol);
-        bh_arr_pop(parser->current_symbol_stack);
-        if (parser->hit_unexpected_token) return 2;
-
-        ENTITY_SUBMIT(binding);
-        return 2;
-    }
-
-    AstType* type_node = NULL;
-    if (parser->curr->type != '=') {
-        type_node = parse_type(parser);
-    }
-
-    AstLocal* local = make_local(parser->allocator, symbol, type_node);
-    *ret = (AstNode *) local;
-
-    if (parser->curr->type == '=') {
-        AstBinaryOp* assignment = make_node(AstBinaryOp, Ast_Kind_Binary_Op);
-        assignment->operation = Binary_Op_Assign;
-        assignment->token = expect_token(parser, '=');
-        local->next = (AstNode *) assignment;
-
-        AstTyped* expr = parse_expression(parser, 1);
-        if (expr == NULL) return 1;
-        assignment->right = expr;
-
-        // INVESTIGATE: I don't know why, but appearantly, this has to be a
-        // symbol node, not a direct link to the local. There is an error about
-        // being unable to resolve the type of the local if it is immediately set.
-        AstNode* left_symbol = make_node(AstNode, Ast_Kind_Symbol);
-        left_symbol->token = symbol;
-        assignment->left = (AstTyped *) left_symbol;
-    }
-
-    return 1;
-}
-
-static AstReturn* parse_return_stmt(OnyxParser* parser) {
-    AstReturn* return_node = make_node(AstReturn, Ast_Kind_Return);
-    return_node->token = expect_token(parser, Token_Type_Keyword_Return);
-
-    AstTyped* expr = NULL;
-
-    if (parser->curr->type != ';') {
-        expr = parse_compound_expression(parser, 0);
-
-        if (expr == NULL || expr == (AstTyped *) &error_node) {
-            return (AstReturn *) &error_node;
-        } else {
-            return_node->expr = expr;
-        }
-    }
-
-    return return_node;
-}
-
-static AstNode* parse_use_stmt(OnyxParser* parser) {
-    OnyxToken* use_token = expect_token(parser, Token_Type_Keyword_Use);
-    AstUse* use_node = make_node(AstUse, Ast_Kind_Use);
-    use_node->token = use_token;
-    use_node->expr = parse_expression(parser, 1);
-
-    if (consume_token_if_next(parser, '{')) {
-        bh_arr_new(global_heap_allocator, use_node->only, 4);
-
-        while (!consume_token_if_next(parser, '}')) {
-            if (parser->hit_unexpected_token) return NULL;
-
-            QualifiedUse qu;
-            qu.as_name = expect_token(parser, Token_Type_Symbol);
-            qu.symbol_name = qu.as_name;
-
-            if (consume_token_if_next(parser, ':')) {
-                expect_token(parser, ':');
-                qu.symbol_name = expect_token(parser, Token_Type_Symbol);
-            }
-
-            bh_arr_push(use_node->only, qu);
-
-            if (parser->curr->type != '}')
-                expect_token(parser, ',');
-        }
-    }
-
-    if (use_node->expr->kind == Ast_Kind_Package) {
-        ENTITY_SUBMIT(use_node);
-        return NULL;
-
-    } else {
-        return (AstNode *) use_node;
-    }
-}
-
-static AstNode* parse_jump_stmt(OnyxParser* parser, TokenType token_type, JumpType jump_type) {
-    AstJump* jnode = make_node(AstJump, Ast_Kind_Jump);
-    jnode->token = expect_token(parser, token_type);
-    jnode->jump  = jump_type;
-
-    u64 count = 1;
-    while (consume_token_if_next(parser, token_type)) count++;
-    jnode->count = count;
-
-    return (AstNode *) jnode;
-}
-
-static AstNode* parse_statement(OnyxParser* parser) {
-    b32 needs_semicolon = 1;
-    AstNode* retval = NULL;
-
-    switch ((u16) parser->curr->type) {
-        case Token_Type_Keyword_Return:
-            retval = (AstNode *) parse_return_stmt(parser);
-            break;
-
-        case '{':
-        case Token_Type_Empty_Block:
-        case Token_Type_Keyword_Do:
-            needs_semicolon = 0;
-            retval = (AstNode *) parse_block(parser, 1, NULL);
-            break;
-
-        case Token_Type_Symbol: {
-            i32 symbol_res = parse_possible_symbol_declaration(parser, &retval);
-            if (symbol_res == 2) needs_semicolon = 0;
-            if (symbol_res != 0) break;
-
-            // fallthrough
-        }
-
-        case '(': case '+': case '-': case '!': case '*': case '^':
-        case Token_Type_Literal_Integer:
-        case Token_Type_Literal_Float:
-        case Token_Type_Literal_String:
-            retval = (AstNode *) parse_compound_expression(parser, 1);
-            if (retval->kind == Ast_Kind_Call || retval->kind == Ast_Kind_Method_Call) {
-                if (parser->curr->type == '{') {
-                    AstCodeBlock* code_block = make_node(AstCodeBlock, Ast_Kind_Code_Block);
-                    code_block->token = parser->curr;
-                    code_block->type_node = builtin_code_type;
-
-                    code_block->code = (AstNode *) parse_block(parser, 1, NULL);
-                    ((AstBlock *) code_block->code)->rules = Block_Rule_Code_Block;
-
-                    AstCall *dest = (AstCall *) retval;
-                    if (dest->kind == Ast_Kind_Method_Call) {
-                        dest = (AstCall *) ((AstBinaryOp *) dest)->right;
-                        if (dest->kind != Ast_Kind_Call) {
-                            onyx_report_error(retval->token->pos, Error_Critical, "Expected function call on right side of '->'.");
-                            needs_semicolon = 0;
-                            break;
-                        }
-                    }
-
-                    bh_arr_push(dest->args.values, (AstTyped *) make_argument(context.ast_alloc, (AstTyped *) code_block));
-                    needs_semicolon = 0;
-                }
-            }
-            break;
-
-        case Token_Type_Keyword_If:
-            needs_semicolon = 0;
-            retval = (AstNode *) parse_if_stmt(parser);
-            break;
-
-        case Token_Type_Keyword_While:
-            needs_semicolon = 0;
-            retval = (AstNode *) parse_while_stmt(parser);
-            break;
-
-        case Token_Type_Keyword_For:
-            needs_semicolon = 0;
-            retval = (AstNode *) parse_for_stmt(parser);
-            break;
-
-        case Token_Type_Keyword_Switch:
-            needs_semicolon = 0;
-            retval = (AstNode *) parse_switch_stmt(parser);
-            break;
-
-        case Token_Type_Keyword_Case:
-            needs_semicolon = 0;
-            retval = (AstNode *) parse_case_stmt(parser);
-            break;
-
-        case Token_Type_Keyword_Break:
-            retval = parse_jump_stmt(parser, Token_Type_Keyword_Break, Jump_Type_Break);
-            break;
-
-        case Token_Type_Keyword_Continue:
-            retval = parse_jump_stmt(parser, Token_Type_Keyword_Continue, Jump_Type_Continue);
-            break;
-
-        case Token_Type_Keyword_Fallthrough:
-            retval = parse_jump_stmt(parser, Token_Type_Keyword_Fallthrough, Jump_Type_Fallthrough);
-            break;
-
-        case Token_Type_Keyword_Defer: {
-            needs_semicolon = 0;
-
-            AstDefer* defer = make_node(AstDefer, Ast_Kind_Defer);
-            defer->token = expect_token(parser, Token_Type_Keyword_Defer);
-            defer->stmt  = parse_statement(parser);
-
-            retval = (AstNode *) defer;
-            break;
-        }
-
-        case Token_Type_Keyword_Use: {
-            needs_semicolon = 0;
-
-            retval = (AstNode *) parse_use_stmt(parser);
-            break;
-        }
-
-        case '#': {
-            if (parse_possible_directive(parser, "context_scope")) {
-                // :LinearTokenDependent
-                OnyxToken* directive_token = parser->curr - 2;
-
-                OnyxToken* sym_token = bh_alloc_item(parser->allocator, OnyxToken);
-                sym_token->type = Token_Type_Symbol;
-                sym_token->length = 15;
-                sym_token->text = bh_strdup(parser->allocator, "__saved_context ");
-                sym_token->pos = ((OnyxFilePos) {0});
-
-                AstNode *sym_node = make_symbol(parser->allocator, sym_token);
-
-                AstLocal* context_tmp = make_local(parser->allocator, sym_token, NULL);
-                retval = (AstNode *) context_tmp;
-
-                AstBinaryOp* assignment = make_node(AstBinaryOp, Ast_Kind_Binary_Op);
-                assignment->token = directive_token;
-                assignment->operation = Binary_Op_Assign;
-                assignment->left = (AstTyped *) sym_node;
-                assignment->right = builtin_context_variable;
-                context_tmp->next = (AstNode *) assignment;
-
-                AstBinaryOp* assignment2 = make_node(AstBinaryOp, Ast_Kind_Binary_Op);
-                assignment2->token = directive_token + 1;
-                assignment2->operation = Binary_Op_Assign;
-                assignment2->left = builtin_context_variable;
-                assignment2->right = (AstTyped *) sym_node;
-
-                AstDefer* defer_node = make_node(AstDefer, Ast_Kind_Defer);
-                defer_node->token = directive_token;
-                defer_node->stmt = (AstNode *) assignment2;
-                assignment->next = (AstNode *) defer_node;
-
-                AstBlock* context_block = parse_block(parser, 1, NULL);
-                defer_node->next = context_block->body;
-                context_block->body = (AstNode *) context_tmp;
-
-                needs_semicolon = 0;
-                break;
-            }
-
-            if (next_tokens_are(parser, 2, '#', Token_Type_Keyword_If)) {
-                AstIf* static_if = parse_static_if_stmt(parser, 1);
-
-                assert(parser->current_function_stack && bh_arr_length(parser->current_function_stack) > 0);
-                bh_arr_push(bh_arr_last(parser->current_function_stack)->nodes_that_need_entities_after_clone, (AstNode *) static_if);
-
-                needs_semicolon = 0;
-                retval = (AstNode *) static_if;
-                break;
-            }
-
-            if (parse_possible_directive(parser, "persist")) {
-                b32 thread_local = parse_possible_directive(parser, "thread_local");
-
-                OnyxToken* symbol = expect_token(parser, Token_Type_Symbol);
-                AstMemRes* memres = parse_memory_reservation(parser, symbol, thread_local);
-
-                AstBinding* binding = make_node(AstBinding, Ast_Kind_Binding);
-                binding->token = memres->token;
-                binding->node = (AstNode *) memres;
-                ENTITY_SUBMIT(binding);
-                break;
-            }
-
-            if (parse_possible_directive(parser, "remove")) {
-                // :LinearTokenDependent
-                AstDirectiveRemove *remove = make_node(AstDirectiveRemove, Ast_Kind_Directive_Remove);
-                remove->token = parser->curr - 2;
-                retval = (AstNode *) remove;
-                break;
-            }
-
-            if (next_tokens_are(parser, 2, '#', Token_Type_Symbol)) {
-                retval = (AstNode *) parse_factor(parser);
-                break;
-            }
-        }
-
-        default:
-            break;
-    }
-
-    if (needs_semicolon) expect_token(parser, ';');
-
-    return retval;
-}
-
-static AstBlock* parse_block(OnyxParser* parser, b32 make_a_new_scope, char* block_name) {
-    AstBlock* block = make_node(AstBlock, Ast_Kind_Block);
-    block->rules = Block_Rule_Normal;
-
-    // NOTE: --- is for an empty block
-    if (parser->curr->type == Token_Type_Empty_Block) {
-        block->token = expect_token(parser, Token_Type_Empty_Block);
-        return block;
-    }
-
-    if (make_a_new_scope) {
-        block->binding_scope = scope_create(parser->allocator, parser->current_scope, parser->curr->pos);
-        block->binding_scope->name = block_name;
-        parser->current_scope = block->binding_scope;
-    }
-
-    if (parser->curr->type == Token_Type_Keyword_Do) {
-        block->token = expect_token(parser, Token_Type_Keyword_Do);
-        block->body = parse_statement(parser);
-        if (make_a_new_scope) parser->current_scope = parser->current_scope->parent;
-        return block;
-    }
-
-    block->token = expect_token(parser, '{');
-
-    AstNode** next = &block->body;
-    AstNode* stmt = NULL;
-    while (!consume_token_if_next(parser, '}')) {
-        if (parser->hit_unexpected_token) {
-            if (make_a_new_scope) parser->current_scope = parser->current_scope->parent;
-            return block;
-        }
-
-        stmt = parse_statement(parser);
-
-        if (stmt != NULL && stmt->kind != Ast_Kind_Error) {
-            *next = stmt;
-
-            while (stmt->next != NULL) stmt = stmt->next;
-            next = &stmt->next;
-        }
-    }
-
-    if (make_a_new_scope) parser->current_scope = parser->current_scope->parent;
-    return block;
-}
-
-static void parse_polymorphic_variable(OnyxParser* parser, AstType*** next_insertion) {
-    bh_arr(AstPolyParam) pv = NULL;
-
-    if (parser->polymorph_context.poly_params == NULL)
-        onyx_report_error(parser->curr->pos, Error_Critical, "Polymorphic variable not valid here.");
-    else
-        pv = *parser->polymorph_context.poly_params;
-
-    consume_token(parser);
-
-    AstNode* symbol_node = make_node(AstNode, Ast_Kind_Symbol);
-    symbol_node->token = expect_token(parser, Token_Type_Symbol);
-
-    **next_insertion = (AstType *) symbol_node;
-    *next_insertion = NULL;
-
-    if (pv != NULL) {
-        bh_arr_push(pv, ((AstPolyParam) {
-            .kind     = PPK_Poly_Type,
-            .poly_sym = symbol_node,
-
-            // These will be filled out by function_params()
-            .type_expr = NULL,
-            .idx = -1,
-        }));
-
-        *parser->polymorph_context.poly_params = pv;
-    }
-}
-
-static AstType* parse_compound_type(OnyxParser* parser) {
-    // CLEANUP this is little weird having this here because it means that this parses:
-    //
-    //      foo :: (x: (something_here: i32)) -> void ---
-    //
-    if (next_tokens_are(parser, 2, Token_Type_Symbol, ':')) {
-        consume_tokens(parser, 2);
-    }
-
-    AstType* first = parse_type(parser);
-
-    if (parser->curr->type == ',') {
-        AstCompoundType* ctype = make_node(AstCompoundType, Ast_Kind_Type_Compound);
-        ctype->token = parser->curr;
-
-        bh_arr_new(global_heap_allocator, ctype->types, 2);
-        bh_arr_push(ctype->types, first);
-
-        while (consume_token_if_next(parser, ',')) {
-            if (parser->hit_unexpected_token) return (AstType *) ctype;
-
-            if (next_tokens_are(parser, 2, Token_Type_Symbol, ':')) {
-                consume_tokens(parser, 2);
-            }
-
-            bh_arr_push(ctype->types, parse_type(parser));
-        }
-
-        return (AstType *) ctype;
-
-    } else {
-        return first;
-    }
-}
-
-static AstType* parse_function_type(OnyxParser* parser, OnyxToken* proc_token) {
-    bh_arr(AstType *) params = NULL;
-    bh_arr_new(global_scratch_allocator, params, 4);
-    bh_arr_set_length(params, 0);
-
-    expect_token(parser, '(');
-    while (!consume_token_if_next(parser, ')')) {
-        if (parser->hit_unexpected_token) return NULL;
-
-        // NOTE: Allows for names to be put in the function types, just for readability.
-        if (next_tokens_are(parser, 2, Token_Type_Symbol, ':')) consume_tokens(parser, 2);
-
-        AstType* param_type = parse_type(parser);
-        bh_arr_push(params, param_type);
-
-        if (parser->curr->type != ')')
-            expect_token(parser, ',');
-    }
-
-    AstType* return_type = (AstType *) &basic_type_void;
-    if (consume_token_if_next(parser, Token_Type_Right_Arrow))
-        return_type = parse_type(parser);
-
-    i64 param_count = bh_arr_length(params);
-    AstFunctionType* new = onyx_ast_node_new(parser->allocator,
-            sizeof(AstFunctionType) + sizeof(AstType*) * param_count,
-            Ast_Kind_Function_Type);
-    new->token = proc_token;
-    new->param_count = param_count;
-    new->return_type = return_type;
-
-    if (param_count > 0)
-        fori (i, 0, param_count) new->params[i] = params[i];
-
-    return (AstType *) new;
-}
-
-static AstType* parse_type(OnyxParser* parser) {
-    AstType* root = NULL;
-    AstType** next_insertion = &root;
-
-    while (1) {
-        if (parser->hit_unexpected_token) return root;
-
-        switch ((u16) parser->curr->type) {
-            case '^': {
-                AstPointerType* new = make_node(AstPointerType, Ast_Kind_Pointer_Type);
-                new->flags |= Basic_Flag_Pointer;
-                new->token = expect_token(parser, '^');
-
-                *next_insertion = (AstType *) new;
-                next_insertion = &new->elem;
-                break;
-            }
-
-            case '[': {
-                AstType *new;
-                OnyxToken *open_bracket = expect_token(parser, '[');
-
-                if (parser->curr->type == ']') {
-                    new = make_node(AstSliceType, Ast_Kind_Slice_Type);
-                    new->token = open_bracket;
-
-                } else if (parser->curr->type == Token_Type_Dot_Dot) {
-                    new = make_node(AstDynArrType, Ast_Kind_DynArr_Type);
-                    new->token = open_bracket;
-                    consume_token(parser);
-
-                } else {
-                    new = make_node(AstArrayType, Ast_Kind_Array_Type);
-                    new->token = open_bracket;
-
-                    if (parser->curr->type == '$') {
-                        AstType** insertion = (AstType **) &((AstArrayType *) new)->count_expr;
-                        parse_polymorphic_variable(parser, &insertion);
-                    } else {
-                        ((AstArrayType *) new)->count_expr = parse_expression(parser, 0);
-                    }
-                }
-
-                expect_token(parser, ']');
-                *next_insertion = (AstType *) new;
-                next_insertion = &((AstSliceType *) new)->elem;
-                break;
-            }
-
-            case '$': {
-                parse_polymorphic_variable(parser, &next_insertion);
-                break;
-            }
-
-            case Token_Type_Symbol: {
-                AstNode* symbol_node = make_node(AstNode, Ast_Kind_Symbol);
-                symbol_node->token = expect_token(parser, Token_Type_Symbol);
-
-                *next_insertion = (AstType *) symbol_node;
-
-                while (consume_token_if_next(parser, '.')) {
-                    AstFieldAccess* field = make_node(AstFieldAccess, Ast_Kind_Field_Access);
-                    field->token = expect_token(parser, Token_Type_Symbol);
-                    field->expr  = (AstTyped *) *next_insertion;
-
-                    *next_insertion = (AstType *) field;
-                }
-
-                if (parser->curr->type == '(' && parser->parse_calls) {
-                    OnyxToken* paren_token = expect_token(parser, '(');
-
-                    bh_arr(AstNode *) params = NULL;
-                    bh_arr_new(global_heap_allocator, params, 2);
-
-                    while (!consume_token_if_next(parser, ')')) {
-                        if (parser->hit_unexpected_token) break;
-
-                        AstNode* t = (AstNode *) parse_expression(parser, 0);
-                        bh_arr_push(params, t);
-
-                        if (parser->curr->type != ')')
-                            expect_token(parser, ',');
-                    }
-
-                    AstPolyCallType* pc_type = make_node(AstPolyCallType, Ast_Kind_Poly_Call_Type);
-                    pc_type->token = paren_token;
-                    pc_type->callee = *next_insertion;
-                    pc_type->params = params;
-
-                    *next_insertion = (AstType *) pc_type;
-                }
-
-                next_insertion = NULL;
-                break;
-            }
-
-            case Token_Type_Keyword_Struct: {
-                AstStructType* s_node = parse_struct(parser);
-                *next_insertion = (AstType *) s_node;
-                next_insertion = NULL;
-                break;
-            }
-
-            //
-            // I don't think any of these cases are necesary any more?
-            case Token_Type_Literal_Integer:
-            case Token_Type_Literal_String:
-            case Token_Type_Literal_Float:
-            case Token_Type_Literal_True:
-            case Token_Type_Literal_False:
-            case '-': {
-                *next_insertion = (AstType *) parse_expression(parser, 0);
-                next_insertion = NULL;
-                break;
-            }
-
-            case '(': {
-                OnyxToken* matching = find_matching_paren(parser->curr);
-
-                // :LinearTokenDependent
-                if ((matching + 1)->type == Token_Type_Right_Arrow) {
-                    *next_insertion = parse_function_type(parser, parser->curr);
-
-                } else {
-                    expect_token(parser, '(');
-                    *next_insertion = parse_compound_type(parser);
-                    expect_token(parser, ')');
-                }
-
-                next_insertion = NULL;
-                break;
-            }
-
-            case Token_Type_Keyword_Typeof: {
-                *next_insertion = (AstType *) parse_typeof(parser);
-                next_insertion = NULL;
-                break;
-            }
-
-            default:
-                onyx_report_error(parser->curr->pos, Error_Critical, "unexpected token '%b'.", parser->curr->text, parser->curr->length);
-                consume_token(parser);
-                break;
-        }
-
-        if (next_insertion == NULL) break;
-    }
-
-    return root;
-}
-
-static AstTypeOf* parse_typeof(OnyxParser* parser) {
-    OnyxToken* token = expect_token(parser, Token_Type_Keyword_Typeof);
-
-    AstTypeOf* type_of = make_node(AstTypeOf, Ast_Kind_Typeof);
-    type_of->token = token;
-    type_of->expr = parse_expression(parser, 0);
-    type_of->resolved_type = NULL;
-
-    return type_of;
-}
-
-static void struct_type_create_scope(OnyxParser *parser, AstStructType *s_node) {
-    if (!s_node->scope) {
-        s_node->scope = scope_create(context.ast_alloc, parser->current_scope, s_node->token->pos);
-        parser->current_scope = s_node->scope;
-
-        OnyxToken* current_symbol = bh_arr_last(parser->current_symbol_stack);
-        s_node->scope->name = bh_aprintf(global_heap_allocator, "%b", current_symbol->text, current_symbol->length);
-    }
-}
-
-static AstStructType* parse_struct(OnyxParser* parser) {
-    OnyxToken *s_token = expect_token(parser, Token_Type_Keyword_Struct);
-
-    AstStructType* s_node;
-    AstPolyStructType* poly_struct = NULL;
-
-    s_node = make_node(AstStructType, Ast_Kind_Struct_Type);
-    s_node->token = s_token;
-
-    flush_stored_tags(parser, &s_node->meta_tags);
-
-    // Parse polymorphic parameters
-    if (consume_token_if_next(parser, '(')) {
-        bh_arr(AstPolyStructParam) poly_params = NULL;
-        bh_arr_new(global_heap_allocator, poly_params, 1);
-
-        while (!consume_token_if_next(parser, ')')) {
-            if (parser->hit_unexpected_token) return NULL;
-
-            OnyxToken* sym_token = expect_token(parser, Token_Type_Symbol);
-            expect_token(parser, ':');
-
-            AstType* param_type = parse_type(parser);
-
-            bh_arr_push(poly_params, ((AstPolyStructParam) {
-                .token = sym_token,
-                .type_node = param_type,
-                .type = NULL,
-            }));
-
-            if (parser->curr->type != ')')
-                expect_token(parser, ',');
-        }
-
-        poly_struct = make_node(AstPolyStructType, Ast_Kind_Poly_Struct_Type);
-        poly_struct->token = s_token;
-        poly_struct->poly_params = poly_params;
-        poly_struct->base_struct = s_node;
-    }
-
-    // Parse constraints clause
-    if (parser->curr->type == Token_Type_Keyword_Where) {
-        parse_constraints(parser, &s_node->constraints);
-    }
-
-    bh_arr_new(global_heap_allocator, s_node->members, 4);
-
-    // Parse directives
-    while (parser->curr->type == '#') {
-        if (parser->hit_unexpected_token) return NULL;
-
-        if (parse_possible_directive(parser, "union")) s_node->is_union = 1;
-
-        else if (parse_possible_directive(parser, "pack")) s_node->is_packed = 1;
-
-        else if (parse_possible_directive(parser, "align")) {
-            s_node->min_alignment_ = parse_expression(parser, 0);
-        }
-
-        else if (parse_possible_directive(parser, "size")) {
-            s_node->min_size_ = parse_expression(parser, 0);
-        }
-
-        else {
-            OnyxToken* directive_token = expect_token(parser, '#');
-            OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
-
-            onyx_report_error(directive_token->pos, Error_Critical, "unknown directive '#%b'.", symbol_token->text, symbol_token->length);
-        }
-    }
-
-    expect_token(parser, '{');
-
-    b32 member_is_used = 0;
-    bh_arr(OnyxToken *) member_list_temp = NULL;
-    bh_arr_new(global_heap_allocator, member_list_temp, 4);
-
-    while (!consume_token_if_next(parser, '}')) {
-        if (parser->hit_unexpected_token) return s_node;
-
-        if (parse_possible_directive(parser, "persist")) {
-            struct_type_create_scope(parser, s_node);
-            
-            b32 thread_local = parse_possible_directive(parser, "thread_local");
-
-            OnyxToken* symbol = expect_token(parser, Token_Type_Symbol);
-            AstMemRes* memres = parse_memory_reservation(parser, symbol, thread_local);
-            consume_token_if_next(parser, ';');
-
-            AstBinding* binding = make_node(AstBinding, Ast_Kind_Binding);
-            binding->token = memres->token;
-            binding->node = (AstNode *) memres;
-            ENTITY_SUBMIT(binding);
-            continue;
-        }
-
-        if (next_tokens_are(parser, 3, Token_Type_Symbol, ':', ':')) {
-            struct_type_create_scope(parser, s_node);
-
-            OnyxToken* binding_name = expect_token(parser, Token_Type_Symbol);
-            consume_token(parser);
-
-            AstBinding* binding = parse_top_level_binding(parser, binding_name);
-            if (binding) ENTITY_SUBMIT(binding);
-
-            consume_token_if_next(parser, ';');
-            continue;
-        }
-
-        bh_arr(AstTyped *) meta_tags=NULL;
-        while (parse_possible_directive(parser, "tag")) {
-            if (meta_tags == NULL) bh_arr_new(global_heap_allocator, meta_tags, 1);
-
-            parser->tag_depth += 1;
-
-            do {
-                AstTyped* expr = parse_expression(parser, 0);
-                bh_arr_push(meta_tags, expr);
-            } while (consume_token_if_next(parser, ','));
-
-            parser->tag_depth -= 1;
-        }
-
-        member_is_used = consume_token_if_next(parser, Token_Type_Keyword_Use);
-
-        bh_arr_clear(member_list_temp);
-        while (!consume_token_if_next(parser, ':')) {
-            if (parser->hit_unexpected_token) return NULL;
-            bh_arr_push(member_list_temp, expect_token(parser, Token_Type_Symbol));
-
-            if (parser->curr->type != ':')
-                expect_token(parser, ',');
-        }
-
-        AstType* member_type = NULL;
-        if (parser->curr->type != '=')
-            member_type = parse_type(parser);
-
-        AstTyped* initial_value = NULL;
-        if (consume_token_if_next(parser, '='))
-            initial_value = parse_expression(parser, 0);
-
-        // RECONSIDER: There are seamingly arbitrary limitations put in place here which do two things:
-        //   1. Prevent multiple struct members being used in the same declaration.
-        //      This makes sense because the members will be of the same type, which means
-        //      they have the same members. Using both of the members would immediately result
-        //      in name collisions.
-        //
-        //   2. Prevent multiple struct members having an initializer set for them.
-        //      I think the semantics could be confusing either way, so I'm deciding to leave
-        //      them out of discussion for now. Initialized members should be treated special and
-        //      deserve their own line.
-        if (bh_arr_length(member_list_temp) > 1) {
-            if (member_is_used) onyx_report_error((member_list_temp[0] - 1)->pos, Error_Critical, "'use' is only allowed for a single struct member declaration. Try splitting this compound declaration into multiple lines.");
-            if (initial_value)  onyx_report_error(initial_value->token->pos, Error_Critical, "Intialized values are only allowed on single struct member declarations. Try splitting this compound initializer into multiple lines.");
-        }
-
-        bh_arr_each(OnyxToken *, member_name, member_list_temp) {
-            AstStructMember* mem = make_node(AstStructMember, Ast_Kind_Struct_Member);
-            mem->token = *member_name;
-            mem->type_node = member_type;
-            mem->initial_value = initial_value;
-            mem->meta_tags = meta_tags;
-
-            if (member_is_used) mem->is_used = 1;
-
-            bh_arr_push(s_node->members, mem);
-        }
-
-        if (peek_token(0)->type != '}') {
-            expect_token(parser, ';');
-        }
-    }
-
-    if (s_node->scope) parser->current_scope = parser->current_scope->parent;
-
-    bh_arr_free(member_list_temp);
-
-    if (poly_struct != NULL) {
-        // NOTE: Not a StructType
-        return (AstStructType *) poly_struct;
-
-    } else {
-        ENTITY_SUBMIT(s_node);
-        return s_node;
-    }
-}
-
-static AstInterface* parse_interface(OnyxParser* parser) {
-    AstInterface *interface = make_node(AstInterface, Ast_Kind_Interface);
-    interface->token = expect_token(parser, Token_Type_Keyword_Interface);
-
-    bh_arr_new(global_heap_allocator, interface->params, 2);
-
-    expect_token(parser, '(');
-    while (!consume_token_if_next(parser, ')')) {
-        if (parser->hit_unexpected_token) return interface;
-
-        InterfaceParam ip;
-        ip.value_token = expect_token(parser, Token_Type_Symbol);
-        expect_token(parser, ':');
-        expect_token(parser, '$');
-        ip.type_token  = expect_token(parser, Token_Type_Symbol);
-
-        bh_arr_push(interface->params, ip);
-
-        if (parser->curr->type != ')')
-            expect_token(parser, ',');
-    }
-
-    bh_arr_new(global_heap_allocator, interface->exprs, 2);
-
-    expect_token(parser, '{');
-    while (!consume_token_if_next(parser, '}')) {
-        if (parser->hit_unexpected_token) return interface;
-
-        InterfaceConstraint ic = {0};
-        if (parse_possible_directive(parser, "not")) {
-            ic.invert_condition = 1;
-        }
-
-        if (consume_token_if_next(parser, '{')) {
-            ic.expr = parse_expression(parser, 0);
-
-            expect_token(parser, '}');
-            expect_token(parser, Token_Type_Right_Arrow);
-
-            ic.expected_type_expr = parse_type(parser);
-
-        } else {
-            ic.expr = parse_expression(parser, 0);
-        }
-
-        bh_arr_push(interface->exprs, ic);
-
-        expect_token(parser, ';');
-    }
-
-    return interface;
-}
-
-static AstConstraint* parse_constraint(OnyxParser* parser) {
-    AstConstraint* constraint = make_node(AstConstraint, Ast_Kind_Constraint);
-
-    parser->parse_calls = 0;
-    constraint->interface = (AstInterface *) parse_factor(parser);
-    parser->parse_calls = 1;
-
-    constraint->token = constraint->interface->token;
-
-    bh_arr_new(global_heap_allocator, constraint->type_args, 2);
-
-    expect_token(parser, '(');
-    while (!consume_token_if_next(parser, ')')) {
-        if (parser->hit_unexpected_token) return constraint;
-
-        AstType* type_node = parse_type(parser);
-        bh_arr_push(constraint->type_args, type_node);
-
-        if (parser->curr->type != ')')
-            expect_token(parser, ',');
-    }
-
-    return constraint;
-}
-
-static void parse_constraints(OnyxParser* parser, ConstraintContext *out_constraints) {
-    bh_arr_new(global_heap_allocator, out_constraints->constraints, 2);
-
-    expect_token(parser, Token_Type_Keyword_Where);
-
-    do {
-        AstConstraint *constraint = parse_constraint(parser);
-        if (parser->hit_unexpected_token) return;
-
-        bh_arr_push(out_constraints->constraints, constraint);
-    } while (consume_token_if_next(parser, ','));
-}
-
-static void parse_function_params(OnyxParser* parser, AstFunction* func) {
-    expect_token(parser, '(');
-
-    if (consume_token_if_next(parser, ')')) return;
-
-    u32 param_idx = 0;
-    assert(parser->polymorph_context.poly_params != NULL);
-
-    bh_arr(AstParam) param_buffer=NULL;
-    bh_arr_new(global_heap_allocator, param_buffer, 2);
-
-    OnyxToken* symbol;
-    while (!consume_token_if_next(parser, ')')) {
-        do {
-            if (parser->hit_unexpected_token) return;
-
-            b32 param_use = 0;
-            b32 param_is_baked = 0;
-            AstParam curr_param = { 0 };
-
-            if (consume_token_if_next(parser, Token_Type_Keyword_Use)) param_use = 1;
-            if (consume_token_if_next(parser, '$'))                    param_is_baked = 1;
-
-            symbol = expect_token(parser, Token_Type_Symbol);
-
-            curr_param.vararg_kind = VA_Kind_Not_VA;
-            curr_param.local = make_local(parser->allocator, symbol, NULL);
-            curr_param.local->kind = Ast_Kind_Param;
-
-            if (param_use) {
-                curr_param.is_used = 1;
-                param_use = 0;
-            }
-
-            if (param_is_baked) {
-                curr_param.is_baked = 1;
-                param_is_baked = 0;
-            }
-
-            bh_arr_push(param_buffer, curr_param);
-        } while (consume_token_if_next(parser, ','));
-
-        expect_token(parser, ':');
-
-        VarArgKind vararg_kind=VA_Kind_Not_VA;
-        AstType* type_node=NULL;
-        AstTyped* default_value=NULL;
-
-        if (parser->curr->type != '=') {
-            if (consume_token_if_next(parser, Token_Type_Dot_Dot)) {
-                if (consume_token_if_next(parser, '.')) vararg_kind = VA_Kind_Untyped;
-                else                                    vararg_kind = VA_Kind_Typed;
-            }
-
-            if (vararg_kind != VA_Kind_Untyped) {
-                // CLEANUP: This is mess and it is hard to follow what is going on here.
-                // I think with recent rewrites, this should be easier to do.
-                i32 old_len = bh_arr_length(*parser->polymorph_context.poly_params);
-                type_node = parse_type(parser);
-                i32 new_len = bh_arr_length(*parser->polymorph_context.poly_params);
-
-                if (vararg_kind == VA_Kind_Typed) {
-                    AstVarArgType* va_type = make_node(AstVarArgType, Ast_Kind_VarArg_Type);
-                    va_type->elem = type_node;
-                    va_type->token = type_node->token;
-                    type_node = (AstType *) va_type;
-                }
-
-                fori (i, 0, new_len - old_len) {
-                    (*parser->polymorph_context.poly_params)[old_len + i].type_expr = type_node;
-                    (*parser->polymorph_context.poly_params)[old_len + i].idx = param_idx;
-                }
-            }
-        }
-
-        if (vararg_kind == VA_Kind_Not_VA && consume_token_if_next(parser, '=')) {
-            OnyxToken* directive_token = parser->curr;
-
-            // :Callsite  currently #callsite is only valid as a default value for a funciton parameter.
-            if (parse_possible_directive(parser, "callsite")) {
-                AstCallSite* cs = make_node(AstCallSite, Ast_Kind_Call_Site);
-                cs->token = directive_token;
-                default_value = (AstTyped *) cs;
-
-            } else {
-                default_value = parse_expression(parser, 0);
-            }
-        }
-
-        bh_arr_each(AstParam, param, param_buffer) {
-            param->vararg_kind      = vararg_kind;
-            param->local->type_node = type_node;
-            param->default_value    = default_value;
-
-            if (param->is_baked) {
-                bh_arr(AstPolyParam) pv = *parser->polymorph_context.poly_params;
-                bh_arr_push(pv, ((AstPolyParam) {
-                    .kind = PPK_Baked_Value,
-                    .idx = param_idx,
-
-                    .poly_sym = (AstNode *) param->local,
-                    .type_expr = type_node,
-                }));
-
-                *parser->polymorph_context.poly_params = pv;
-            }
-
-            bh_arr_push(func->params, *param);
-            param_idx++;
-        }
-
-        bh_arr_clear(param_buffer);
-
-        if (parser->curr->type != ')')
-            expect_token(parser, ',');
-    }
-
-    bh_arr_free(param_buffer);
-    return;
-}
-
-static AstOverloadedFunction* parse_overloaded_function(OnyxParser* parser, OnyxToken* token) {
-    b32 locked = 0;
-    if (parse_possible_directive(parser, "locked")) {
-        locked = 1;
-    }
-
-    b32 local = 0;
-    if (parse_possible_directive(parser, "local")) {
-        local = 1;
-    }
-
-    // This could be checked elsewhere?
-    if (locked && local) {
-        onyx_report_error(token->pos, Error_Critical, "Only one of '#locked' and '#local' can because use at a time.");
-    }
-
-    expect_token(parser, '{');
-
-    AstOverloadedFunction* ofunc = make_node(AstOverloadedFunction, Ast_Kind_Overloaded_Function);
-    ofunc->token = token;
-    ofunc->flags |= Ast_Flag_Comptime;
-    ofunc->locked = locked;
-    ofunc->only_local_functions = local;
-
-    bh_arr_new(global_heap_allocator, ofunc->overloads, 4);
-
-    u64 precedence = 0;
-    while (!consume_token_if_next(parser, '}')) {
-        if (parser->hit_unexpected_token) return ofunc;
-
-        if (parse_possible_directive(parser, "precedence")) {
-            AstNumLit* pre = parse_int_literal(parser);
-            if (parser->hit_unexpected_token) return ofunc;
-
-            precedence = bh_max(pre->value.l, 0);
-        }
-
-        AstTyped* option = parse_expression(parser, 0);
-        add_overload_option(&ofunc->overloads, precedence++, option);
-
-        if (parser->curr->type != '}')
-            expect_token(parser, ',');
-    }
-
-    ENTITY_SUBMIT(ofunc);
-    return ofunc;
-}
-
-static AstFunction* parse_function_definition(OnyxParser* parser, OnyxToken* token) {
-    AstFunction* func_def = make_node(AstFunction, Ast_Kind_Function);
-    func_def->token = token;
-    bh_arr_push(parser->current_function_stack, func_def);
-
-    flush_stored_tags(parser, &func_def->tags);
-
-    bh_arr_new(global_heap_allocator, func_def->params, 4);
-
-    bh_arr(AstPolyParam) polymorphic_vars = NULL;
-    bh_arr_new(global_heap_allocator, polymorphic_vars, 4);
-    // defer bh_arr_free(polymorphic_vars);
-
-    parser->polymorph_context.poly_params = &polymorphic_vars;
-    parse_function_params(parser, func_def);
-    parser->polymorph_context.poly_params = NULL;
-
-    func_def->return_type = (AstType *) &basic_type_void;
-
-    char* name = NULL;
-    if (bh_arr_length(parser->current_symbol_stack) > 0) {
-        OnyxToken *current_symbol = bh_arr_last(parser->current_symbol_stack);
-        name = bh_aprintf(global_heap_allocator, "%b", current_symbol->text, current_symbol->length);
-    }
-
-    if (consume_token_if_next(parser, '=')) {
-        expect_token(parser, '>');
-        func_def->return_type = (AstType *) &basic_type_auto_return;
-
-        if (parser->curr->type == '{') {
-            func_def->body = parse_block(parser, 1, name);
-
-        } else {
-            AstTyped* returned_value = parse_compound_expression(parser, 0);
-            if (returned_value == NULL) goto function_defined;
-
-            AstReturn* return_node = make_node(AstReturn, Ast_Kind_Return);
-            return_node->token = returned_value->token;
-            return_node->expr = returned_value;
-
-            AstBlock* body_block = make_node(AstBlock, Ast_Kind_Block);
-            body_block->token = returned_value->token;
-            body_block->body = (AstNode *) return_node;
-
-            func_def->body = body_block;
-        }
-
-        goto function_defined;
-    }
-
-    if (consume_token_if_next(parser, Token_Type_Right_Arrow)) {
-        if (parse_possible_directive(parser, "auto")) {
-            func_def->return_type = (AstType *) &basic_type_auto_return;
-        } else {
-            func_def->return_type = parse_type(parser);
-        }
-    }
-
-    if (parser->curr->type == Token_Type_Keyword_Where) {
-        parse_constraints(parser, &func_def->constraints);
-    }
-
-    while (parser->curr->type == '#') {
-        if (parse_possible_directive(parser, "intrinsic")) {
-            func_def->is_intrinsic = 1;
-
-            if (parser->curr->type == Token_Type_Literal_String) {
-                func_def->intrinsic_name = expect_token(parser, Token_Type_Literal_String);
-            }
-        }
-
-        else if (parse_possible_directive(parser, "foreign")) {
-            func_def->foreign_module = expect_token(parser, Token_Type_Literal_String);
-            func_def->foreign_name   = expect_token(parser, Token_Type_Literal_String);
-
-            func_def->is_foreign = 1;
-        }
-
-        // HACK: NullProcHack
-        else if (parse_possible_directive(parser, "null")) {
-            func_def->flags |= Ast_Flag_Proc_Is_Null;
-        }
-
-        else {
-            OnyxToken* directive_token = expect_token(parser, '#');
-            OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
-
-            onyx_report_error(directive_token->pos, Error_Critical, "unknown directive '#%b'.", symbol_token->text, symbol_token->length);
-        }
-    }
-
-    func_def->body = parse_block(parser, 1, name);
-
-    // :LinearTokenDependent
-    func_def->closing_brace = parser->curr - 1;
-
-function_defined:
-    if (bh_arr_length(polymorphic_vars) > 0) {
-        func_def->kind = Ast_Kind_Polymorphic_Proc;
-        func_def->poly_params = polymorphic_vars;
-
-    } else {
-        bh_arr_free(polymorphic_vars);
-    }
-
-    bh_arr_pop(parser->current_function_stack);
-    return func_def;
-}
-
-static b32 parse_possible_function_definition_no_consume(OnyxParser* parser) {
-    if (parser->curr->type == '(') {
-        OnyxToken* matching_paren = find_matching_paren(parser->curr);
-        if (matching_paren == NULL) return 0;
-
-        if (next_tokens_are(parser, 4, '(', ')', '=', '>')) return 0;
-
-        // :LinearTokenDependent
-        OnyxToken* token_after_paren = matching_paren + 1;
-        if (token_after_paren->type != Token_Type_Right_Arrow
-            && token_after_paren->type != '{'
-            && token_after_paren->type != Token_Type_Keyword_Do
-            && token_after_paren->type != Token_Type_Empty_Block
-            && token_after_paren->type != Token_Type_Keyword_Where
-            && (token_after_paren->type != '=' || (token_after_paren + 1)->type != '>'))
-            return 0;
-
-        // :LinearTokenDependent
-        b32 is_params = (parser->curr + 1) == matching_paren;
-        OnyxToken* tmp_token = parser->curr;
-        while (!is_params && tmp_token < matching_paren) {
-            if (tmp_token->type == ':') is_params = 1;
-
-            tmp_token++;
-        }
-
-        if (!is_params) return 0;
-
-        return 1;
-    }
-
-    return 0;
-}
-
-static b32 parse_possible_function_definition(OnyxParser* parser, AstTyped** ret) {
-    if (parse_possible_function_definition_no_consume(parser)) {
-        OnyxToken* proc_token = parser->curr;
-        AstFunction* func_node = parse_function_definition(parser, proc_token);
-        *ret = (AstTyped *) func_node;
-
-        return 1;
-    }
-
-    return 0;
-}
-
-typedef struct QuickParam {
-    OnyxToken* token;
-    b32 is_baked;
-} QuickParam;
-
-static b32 parse_possible_quick_function_definition_no_consume(OnyxParser* parser) {
-    //
-    // x => x + 1 case.
-    if (next_tokens_are(parser, 3, Token_Type_Symbol, '=', '>')) {
-        return 1;
-    }
-
-    if (parser->curr->type != '(') return 0;
-
-    OnyxToken* matching_paren = find_matching_paren(parser->curr);
-    if (matching_paren == NULL) return 0;
-
-    // :LinearTokenDependent
-    OnyxToken* token_after_paren = matching_paren + 1;
-    if (token_after_paren->type != '=' || (token_after_paren + 1)->type != '>')
-        return 0;
-
-    return 1;
-}
-
-static b32 parse_possible_quick_function_definition(OnyxParser* parser, AstTyped** ret) {
-    if (!parse_possible_quick_function_definition_no_consume(parser)) return 0;
-
-    bh_arr(QuickParam) params=NULL;
-    bh_arr_new(global_heap_allocator, params, 4);
-    OnyxToken* proc_token;
-
-    if (parser->curr->type == Token_Type_Symbol) {
-        QuickParam param = { 0 };
-        param.token = expect_token(parser, Token_Type_Symbol);
-        proc_token = param.token;
-        bh_arr_push(params, param);
-
-    } else {
-        proc_token = expect_token(parser, '(');
-        while (parser->curr->type != ')') {
-            if (parser->hit_unexpected_token) return 0;
-
-            QuickParam param = { 0 };
-            if (consume_token_if_next(parser, '$')) param.is_baked = 1;
-            param.token = expect_token(parser, Token_Type_Symbol);
-
-            bh_arr_push(params, param);
-
-            if (parser->curr->type != ')') {
-                expect_token(parser, ',');
-            }
-        }
-
-        expect_token(parser, ')');
-    }
-
-    expect_token(parser, '=');
-    expect_token(parser, '>');
-
-    bh_arr(AstNode *) poly_params=NULL;
-    bh_arr_new(global_heap_allocator, poly_params, bh_arr_length(params));
-    bh_arr_each(QuickParam, param, params) {
-        char text[512];
-        memset(text, 0, 512);
-        strncat(text, "__type_", 511);
-        token_toggle_end(param->token);
-        strncat(text, param->token->text, 511);
-        token_toggle_end(param->token);
-
-        OnyxToken* new_token = bh_alloc(parser->allocator, sizeof(OnyxToken));
-        new_token->type = Token_Type_Symbol;
-        new_token->length = 7 + param->token->length;
-        new_token->text = bh_strdup(parser->allocator, text);
-        new_token->pos = param->token->pos;
-
-        AstNode* type_node = make_symbol(parser->allocator, new_token);
-        bh_arr_push(poly_params, type_node);
-    }
-
-    AstFunction* poly_proc = make_node(AstFunction, Ast_Kind_Polymorphic_Proc);
-
-    bh_arr_new(global_heap_allocator, poly_proc->params, bh_arr_length(params));
-    fori (i, 0, bh_arr_length(params)) {
-        AstLocal* param_local = make_local(parser->allocator, params[i].token, (AstType *) poly_params[i]);
-        param_local->kind = Ast_Kind_Param;
-
-        bh_arr_push(poly_proc->params, ((AstParam) {
-            .local = param_local,
-            .default_value = NULL,
-
-            .vararg_kind = 0,
-            .use_processed = 0,
-        }));
-    }
-
-    AstBlock* body_block;
-    AstType*  return_type;
-    bh_arr_push(parser->current_function_stack, poly_proc);
-
-    if (parser->curr->type == '{') {
-        char* name = NULL;
-        if (bh_arr_length(parser->current_symbol_stack) > 0) {
-            OnyxToken *current_symbol = bh_arr_last(parser->current_symbol_stack);
-            name = bh_aprintf(global_heap_allocator, "%b", current_symbol->text, current_symbol->length);
-        }
-
-        body_block = parse_block(parser, 1, name);
-        return_type = (AstType *) &basic_type_auto_return;
-
-    } else {
-        AstTyped* body = parse_compound_expression(parser, 0);
-
-        AstReturn* return_node = make_node(AstReturn, Ast_Kind_Return);
-        return_node->token = body->token;
-        return_node->expr = body;
-
-        body_block = make_node(AstBlock, Ast_Kind_Block);
-        body_block->token = body->token;
-        body_block->body = (AstNode *) return_node;
-
-        AstTypeOf* return_type_of = make_node(AstTypeOf, Ast_Kind_Typeof);
-        return_type_of->token = body->token;
-        return_type_of->expr = body;
-        return_type = (AstType *) return_type_of;
-    }
-
-    poly_proc->token = proc_token;
-    poly_proc->body = body_block;
-    poly_proc->return_type = (AstType *) return_type;
-
-    poly_proc->token = proc_token;
-    bh_arr_new(global_heap_allocator, poly_proc->poly_params, bh_arr_length(params));
-    fori (i, 0, bh_arr_length(params)) {
-        bh_arr_push(poly_proc->poly_params, ((AstPolyParam) {
-            .kind = PSK_Type,
-            .idx  = i,
-            .poly_sym = poly_params[i],
-            .type_expr = (AstType *) poly_params[i],
-            .type = NULL,
-        }));
-
-        if (params[i].is_baked) {
-            // This is not handled currently, as you cannot say f :: ($x: $T) yet, which is what this would have to do.
-        }
-    }
-
-    *ret = (AstTyped *) poly_proc;
-
-    bh_arr_pop(parser->current_function_stack);
-    bh_arr_free(params);
-    bh_arr_free(poly_params);
-    return 1;
-}
-
-static AstTyped* parse_global_declaration(OnyxParser* parser) {
-    expect_no_stored_tags(parser);
-
-    AstGlobal* global_node = make_node(AstGlobal, Ast_Kind_Global);
-    global_node->token = expect_token(parser, Token_Type_Keyword_Global);
-
-    global_node->type_node = parse_type(parser);
-
-    ENTITY_SUBMIT(global_node);
-
-    return (AstTyped *) global_node;
-}
-
-static AstEnumType* parse_enum_declaration(OnyxParser* parser) {
-    expect_no_stored_tags(parser);
-
-    AstEnumType* enum_node = make_node(AstEnumType, Ast_Kind_Enum_Type);
-    enum_node->token = expect_token(parser, Token_Type_Keyword_Enum);
-
-    bh_arr_new(global_heap_allocator, enum_node->values, 4);
-
-    while (parser->curr->type == '#') {
-        if (parser->hit_unexpected_token) return enum_node;
-
-        if (parse_possible_directive(parser, "flags")) {
-            enum_node->is_flags = 1;
-        } else {
-            OnyxToken* directive_token = expect_token(parser, '#');
-            OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
-
-            onyx_report_error(directive_token->pos, Error_Critical, "unknown directive '#%b'.", symbol_token->text, symbol_token->length);
-        }
-    }
-
-    AstType* backing = (AstType *) &basic_type_u32;
-    if (consume_token_if_next(parser, '(')) {
-        AstNode* backing_sym = make_node(AstNode, Ast_Kind_Symbol);
-        backing_sym->token = expect_token(parser, Token_Type_Symbol);
-        backing = (AstType *) backing_sym;
-
-        expect_token(parser, ')');
-    }
-    enum_node->backing = backing;
-
-    expect_token(parser, '{');
-
-    while (!consume_token_if_next(parser, '}')) {
-        if (parser->hit_unexpected_token) return enum_node;
-
-        AstEnumValue* evalue = make_node(AstEnumValue, Ast_Kind_Enum_Value);
-        evalue->token = expect_token(parser, Token_Type_Symbol);
-        evalue->type_node = (AstType *) enum_node;
-
-        if (consume_token_if_next(parser, ':')) {
-            expect_token(parser, ':');
-            evalue->value = parse_expression(parser, 0);
-        }
-
-        expect_token(parser, ';');
-
-        bh_arr_push(enum_node->values, evalue);
-    }
-
-    return enum_node;
-}
-
-static AstIf* parse_static_if_stmt(OnyxParser* parser, b32 parse_block_as_statements) {
-    expect_no_stored_tags(parser);
-
-    AstIf* static_if_node = make_node(AstIf, Ast_Kind_Static_If);
-    static_if_node->token = expect_token(parser, '#');
-    static_if_node->defined_in_scope = parser->current_scope;
-    expect_token(parser, Token_Type_Keyword_If);
-
-    static_if_node->cond = parse_expression(parser, 0);
-
-    bh_arr_new(global_heap_allocator, static_if_node->true_entities, 2);
-    bh_arr_push(parser->alternate_entity_placement_stack, &static_if_node->true_entities);
-
-    if (parse_block_as_statements) {
-        static_if_node->true_stmt = parse_block(parser, 0, NULL);
-
-    } else {
-        expect_token(parser, '{');
-        while (!consume_token_if_next(parser, '}')) {
-            if (parser->hit_unexpected_token) return static_if_node;
-
-            parse_top_level_statement(parser);
-        }
-    }
-
-    bh_arr_pop(parser->alternate_entity_placement_stack);
-
-    if (consume_token_if_next(parser, Token_Type_Keyword_Else)) {
-        bh_arr_new(global_heap_allocator, static_if_node->false_entities, 2);
-        bh_arr_push(parser->alternate_entity_placement_stack, &static_if_node->false_entities);
-
-        if (parse_block_as_statements) {
-            static_if_node->false_stmt = parse_block(parser, 0, NULL);
-
-        } else {
-            expect_token(parser, '{');
-            while (!consume_token_if_next(parser, '}')) {
-                if (parser->hit_unexpected_token) return static_if_node;
-
-                parse_top_level_statement(parser);
-            }
-        }
-
-        bh_arr_pop(parser->alternate_entity_placement_stack);
-    }
-
-    return static_if_node;
-}
-
-static AstMemRes* parse_memory_reservation(OnyxParser* parser, OnyxToken* symbol, b32 threadlocal) {
-    expect_token(parser, ':');
-
-    expect_no_stored_tags(parser);
-
-    AstMemRes* memres = make_node(AstMemRes, Ast_Kind_Memres);
-    memres->threadlocal = threadlocal;
-    memres->token = symbol;
-
-    if (parser->curr->type != '=')
-        memres->type_node = parse_type(parser);
-
-    if (consume_token_if_next(parser, '='))
-        memres->initial_value = parse_expression(parser, 1);
-
-    ENTITY_SUBMIT(memres);
-    return memres;
-}
-
-static AstMacro* parse_macro(OnyxParser* parser) {
-    expect_no_stored_tags(parser);
-
-    AstMacro* macro = make_node(AstMacro, Ast_Kind_Macro);
-    macro->token = expect_token(parser, Token_Type_Keyword_Macro);
-
-    if (parse_possible_function_definition(parser, &macro->body)) {
-        ENTITY_SUBMIT(macro);
-        return macro;
-    }
-
-    if (parse_possible_quick_function_definition(parser, &macro->body)) {
-        ENTITY_SUBMIT(macro);
-        return macro;
-    }
-
-    onyx_report_error(parser->curr->pos, Error_Critical, "'macro' expects to be followed by a producure definition.");
-    return NULL;
-}
-
-static AstDirectiveInit* parse_init_directive(OnyxParser *parser, OnyxToken *token) {
-    AstDirectiveInit *init = make_node(AstDirectiveInit, Ast_Kind_Directive_Init);
-    init->token = token;
-
-    parser->parse_calls = 0;
-    while (parse_possible_directive(parser, "after")) {
-        if (parser->hit_unexpected_token) return init;
-        if (init->dependencies == NULL) bh_arr_new(global_heap_allocator, init->dependencies, 2);
-
-        AstTyped *dependency = parse_expression(parser, 0);
-        bh_arr_push(init->dependencies, (AstDirectiveInit *) dependency);
-    }
-    parser->parse_calls = 1;
-
-    init->init_proc = parse_expression(parser, 0);
-    ENTITY_SUBMIT(init);
-    return init;
-}
-
-static AstForeignBlock* parse_foreign_block(OnyxParser* parser, OnyxToken *token) {
-    // :LinearTokenDependent
-    AstForeignBlock *fb = make_node(AstForeignBlock, Ast_Kind_Foreign_Block);
-    fb->token = token;
-    fb->module_name = expect_token(parser, Token_Type_Literal_String);
-
-    //
-    // This has a fun implication that there cannot be foreign blocks in the builtin
-    // or type_info packages, as those are loaded before foreign_block_type has a value.
-    fb->type_node = foreign_block_type;
-
-    bh_arr_new(global_heap_allocator, fb->captured_entities, 4);
-    bh_arr_push(parser->alternate_entity_placement_stack, &fb->captured_entities);
-
-    expect_token(parser, '{');
-    parse_top_level_statements_until(parser, '}');
-    expect_token(parser, '}');
-
-    bh_arr_pop(parser->alternate_entity_placement_stack);
-    ENTITY_SUBMIT(fb);
-
-    return fb;
-}
-
-static AstTyped* parse_top_level_expression(OnyxParser* parser) {
-    if (parser->curr->type == Token_Type_Keyword_Global)    return parse_global_declaration(parser);
-    if (parser->curr->type == Token_Type_Keyword_Struct)    return (AstTyped *) parse_struct(parser);
-    if (parser->curr->type == Token_Type_Keyword_Interface) return (AstTyped *) parse_interface(parser);
-    if (parser->curr->type == Token_Type_Keyword_Enum)      return (AstTyped *) parse_enum_declaration(parser);
-    if (parser->curr->type == Token_Type_Keyword_Macro)     return (AstTyped *) parse_macro(parser);
-
-    if (parser->curr->type == '#') {
-        if (parse_possible_directive(parser, "type")) {
-            AstTypeAlias* alias = make_node(AstTypeAlias, Ast_Kind_Type_Alias);
-            alias->to = parse_type(parser);
-            return (AstTyped *) alias;
-        }
-
-        if (parse_possible_directive(parser, "match")) {
-            // :LinearTokenDependent
-            OnyxToken* directive_token = parser->curr - 2;
-            AstOverloadedFunction* ofunc = parse_overloaded_function(parser, directive_token);
-            return (AstTyped *) ofunc;
-        }
-
-        if (parse_possible_directive(parser, "init")) {
-            // :LinearTokenDependent
-            AstDirectiveInit *init = parse_init_directive(parser, parser->curr - 2);
-            return (AstTyped *) init;
-        }
-
-        if (parse_possible_directive(parser, "distinct")) {
-            // :LinearTokenDependent
-            AstDistinctType *distinct = make_node(AstDistinctType, Ast_Kind_Distinct_Type);
-            distinct->token = parser->curr - 2;
-            distinct->base_type = parse_type(parser);
-            return (AstTyped *) distinct;
-        }
-
-        if (parse_possible_directive(parser, "foreign")) {
-            AstForeignBlock *foreign = parse_foreign_block(parser, parser->curr - 2);
-            return (AstTyped *) foreign;
-        }
-    }
-
-    return parse_expression(parser, 1);
-}
-
-static char* generate_name_within_scope(OnyxParser* parser, OnyxToken* symbol) {
-    char name[512];
-    memset(name, 0, 512);
-
-    bh_arr(char *) names=NULL;
-    bh_arr_new(global_heap_allocator, names, 4);
-
-    Scope* scope = parser->current_scope;
-    while (scope != NULL) {
-        bh_arr_push(names, scope->name);
-        scope = scope->parent;
-    }
-
-    bh_arr_each(char *, n, names) {
-        if (*n == NULL) continue;
-
-        strncat(name, *n, 511);
-        strncat(name, ".", 511);
-    }
-    bh_arr_free(names);
-
-    return bh_aprintf(global_heap_allocator, "%s%b", name, symbol->text, symbol->length);
-}
-
-static AstBinding* parse_top_level_binding(OnyxParser* parser, OnyxToken* symbol) {
-    OnyxToken *after_second_colon = expect_token(parser, ':');
-    if (after_second_colon) after_second_colon += 1;
-
-    AstTyped* node = parse_top_level_expression(parser);
-    if (parser->hit_unexpected_token || node == NULL)
-        return NULL;
-
-    switch (node->kind) {
-        case Ast_Kind_Function:
-        case Ast_Kind_Polymorphic_Proc: {
-            AstFunction* func = (AstFunction *) node;
-
-            if (func->intrinsic_name == NULL) func->intrinsic_name = symbol;
-
-            func->name = generate_name_within_scope(parser, symbol);
-            break;
-        }
-
-        case Ast_Kind_Macro: {
-            AstMacro* macro = (AstMacro *) node;
-
-            AstFunction* func = (AstFunction *) macro->body;
-            func->name = generate_name_within_scope(parser, symbol);
-            break;
-        }
-
-        case Ast_Kind_Directive_Init: break;
-
-        case Ast_Kind_Global: ((AstGlobal *) node)->name = generate_name_within_scope(parser, symbol);
-
-        case Ast_Kind_Overloaded_Function:
-        case Ast_Kind_StrLit:
-            break;
-
-        case Ast_Kind_Interface:
-        case Ast_Kind_Struct_Type:
-        case Ast_Kind_Poly_Struct_Type:
-        case Ast_Kind_Enum_Type:
-        case Ast_Kind_Distinct_Type:
-            ((AstStructType *) node)->name = generate_name_within_scope(parser, symbol);
-            goto default_case;
-
-        case Ast_Kind_Type_Alias:
-            node->token = symbol;
-            goto default_case;
-
-        case Ast_Kind_Package: goto default_case;
-        case Ast_Kind_NumLit:  goto default_case;
-
-        default: {
-            if (!node_is_type((AstNode *) node)) {
-                AstAlias* alias = make_node(AstAlias, Ast_Kind_Alias);
-                alias->token = node->token;
-                alias->alias = node;
-                node = (AstTyped *) alias;
-            }
-
-default_case:
-            ENTITY_SUBMIT(node);
-        }
-    }
-
-    AstBinding* binding = make_node(AstBinding, Ast_Kind_Binding);
-    binding->token = symbol;
-    binding->node = (AstNode *) node;
-
-    if (after_second_colon) expect_no_stored_tags_pos(parser, after_second_colon->pos);
-    return binding;
-}
-
-static void parse_top_level_statement(OnyxParser* parser) {
-    AstFlags private_kind = 0;
-    if (bh_arr_length(parser->scope_flags) > 0)
-        private_kind = bh_arr_last(parser->scope_flags);
-
-    // :CLEANUP this very repetetive code...
-    if (next_tokens_are(parser, 2, '#', Token_Type_Keyword_Package)) {
-        consume_tokens(parser, 2);
-        private_kind = Ast_Flag_Private_Package;
-        if (parser->curr->type == '{') {
-            bh_arr_push(parser->scope_flags, private_kind);
-
-            expect_token(parser, '{');
-            parse_top_level_statements_until(parser, '}');
-            expect_token(parser, '}');
-
-            bh_arr_pop(parser->scope_flags);
-            return;
-        }
-    }
-    else if (parse_possible_directive(parser, "local")) {
-        private_kind = Ast_Flag_Private_File;
-        if (parser->curr->type == '{') {
-            bh_arr_push(parser->scope_flags, private_kind);
-
-            expect_token(parser, '{');
-            parse_top_level_statements_until(parser, '}');
-            expect_token(parser, '}');
-
-            bh_arr_pop(parser->scope_flags);
-            return;
-        }
-    }
-
-    AstBinding* binding = NULL;
-
-    switch ((u16) parser->curr->type) {
-        case Token_Type_Keyword_Use: {
-            AstNode* use_node = parse_use_stmt(parser);
-            if (use_node) ENTITY_SUBMIT(use_node);
-            return;
-        }
-
-        case Token_Type_Symbol: {
-            OnyxToken* symbol = expect_token(parser, Token_Type_Symbol);
-
-            if (next_tokens_are(parser, 2, ':', ':')) {
-                expect_token(parser, ':');
-
-                bh_arr_push(parser->current_symbol_stack, symbol);
-                binding = parse_top_level_binding(parser, symbol);
-                bh_arr_pop(parser->current_symbol_stack);
-
-                if (binding != NULL) binding->flags |= private_kind;
-
-                goto submit_binding_to_entities;
-            }
-
-            AstMemRes* memres = parse_memory_reservation(parser, symbol, 0);
-
-            binding = make_node(AstBinding, Ast_Kind_Binding);
-            binding->token = symbol;
-            binding->flags |= private_kind;
-            binding->node = (AstNode *) memres;
-
-            goto submit_binding_to_entities;
-        }
-
-        case '(': {
-            AstTyped *retval = NULL;
-            if (parse_possible_function_definition(parser, &retval)) {
-                ENTITY_SUBMIT(retval);
-                return;
-            }
-            if (parse_possible_quick_function_definition(parser, &retval)) {
-                ENTITY_SUBMIT(retval);
-                return;
-            }
-            break;
-        }
-
-        case '#': {
-            if (next_tokens_are(parser, 2, '#', Token_Type_Keyword_If)) {
-                AstIf* static_if = parse_static_if_stmt(parser, 0);
-                ENTITY_SUBMIT(static_if);
-                return;
-            }
-
-            OnyxToken* dir_token = parser->curr;
-
-            if (parse_possible_directive(parser, "load")) {
-                AstInclude* include = make_node(AstInclude, Ast_Kind_Load_File);
-                include->token = dir_token;
-                include->name_node = parse_expression(parser, 0);
-
-                ENTITY_SUBMIT(include);
-                return;
-            }
-            else if (parse_possible_directive(parser, "load_all")) {
-                AstInclude* include = make_node(AstInclude, Ast_Kind_Load_All);
-                include->token = dir_token;
-                include->name_node = parse_expression(parser, 0);
-
-                ENTITY_SUBMIT(include);
-                return;
-            }
-            else if (parse_possible_directive(parser, "load_path")) {
-                AstInclude* include = make_node(AstInclude, Ast_Kind_Load_Path);
-                include->token = dir_token;
-                include->name_node = parse_expression(parser, 0);
-
-                ENTITY_SUBMIT(include);
-                return;
-            }
-            else if (parse_possible_directive(parser, "library_path")) {
-                AstInclude* include = make_node(AstInclude, Ast_Kind_Library_Path);
-                include->token = dir_token;
-                include->name_node = parse_expression(parser, 0);
-
-                ENTITY_SUBMIT(include);
-                return;
-            }
-            else if (parse_possible_directive(parser, "error")) {
-                AstDirectiveError *error = make_node(AstDirectiveError, Ast_Kind_Directive_Error);
-                error->token = dir_token;
-                error->error_msg = expect_token(parser, Token_Type_Literal_String);
-
-                ENTITY_SUBMIT(error);
-                return;
-            }
-            else if (parse_possible_directive(parser, "foreign")) {
-                parse_foreign_block(parser, parser->curr - 2);
-                return;
-            }
-            else if (parse_possible_directive(parser, "operator")) {
-                AstDirectiveOperator *operator = make_node(AstDirectiveOperator, Ast_Kind_Directive_Operator);
-                operator->token = dir_token;
-
-                // These cases have to happen first because these are not necessarily "binary operators",
-                // they are just things that I want to be able to overload. []= is technically a ternary
-                // operator so all these things are horribly named anyway.
-                if (next_tokens_are(parser, 3, '^', '[', ']')) {
-                    consume_tokens(parser, 3);
-                    operator->operator = Binary_Op_Ptr_Subscript;
-                    goto operator_determined;
-                }
-
-                if (next_tokens_are(parser, 3, '[', ']', '=')) {
-                    consume_tokens(parser, 3);
-                    operator->operator = Binary_Op_Subscript_Equals;
-                    goto operator_determined;
-                }
-
-                // The default case
-                BinaryOp op = binary_op_from_token_type(parser->curr->type);
-                consume_token(parser);
-                if (op == Binary_Op_Subscript) expect_token(parser, ']');    // #operator [] ... needs to consume the other ']'
-
-                if (op == Binary_Op_Count) {
-                    onyx_report_error(parser->curr->pos, Error_Critical, "Invalid binary operator.");
-                } else {
-                    operator->operator = op;
-                }
-
-              operator_determined:
-                operator->overload = parse_expression(parser, 0);
-
-                ENTITY_SUBMIT(operator);
-                return;
-            }
-            else if (parse_possible_directive(parser, "match") || parse_possible_directive(parser, "overload")) {
-                AstDirectiveAddOverload *add_overload = make_node(AstDirectiveAddOverload, Ast_Kind_Directive_Add_Overload);
-                add_overload->token = dir_token;
-
-                if (parse_possible_directive(parser, "precedence")) {
-                    AstNumLit* pre = parse_int_literal(parser);
-                    if (parser->hit_unexpected_token) return;
-
-                    add_overload->precedence = bh_max(pre->value.l, 0);
-                } else {
-                    add_overload->precedence = 0;
-                }
-
-                parser->parse_calls = 0;
-                add_overload->overloaded_function = (AstNode *) parse_expression(parser, 0);
-                parser->parse_calls = 1;
-
-                // Allow for
-                //      #match
-                //      something :: (....) {
-                //      }
-                // 
-                // This will make converting something to a overloaded
-                // function easier and require less copying by the programmer.
-                if (next_tokens_are(parser, 2, ':', ':')) {
-                    consume_tokens(parser, 2);
-                }
-
-                add_overload->overload = parse_expression(parser, 0);
-
-                ENTITY_SUBMIT(add_overload);
-                return;
-            }
-            else if (parse_possible_directive(parser, "inject")) {
-                AstInjection *inject = make_node(AstInjection, Ast_Kind_Injection);
-                inject->token = dir_token;
-
-                parser->parse_calls = 0;
-                inject->full_loc = parse_expression(parser, 0);
-                parser->parse_calls = 1;
-
-                // See comment above
-                if (next_tokens_are(parser, 2, ':', ':')) {
-                    consume_tokens(parser, 2);
-                }
-
-                inject->to_inject = parse_expression(parser, 0);
-                
-                ENTITY_SUBMIT(inject);
-                return;
-            }
-            else if (parse_possible_directive(parser, "export")) {
-                AstDirectiveExport *export = make_node(AstDirectiveExport, Ast_Kind_Directive_Export);
-                export->token = dir_token;
-                parser->parse_calls = 0;
-                export->export_name_expr = parse_expression(parser, 0); // expect_token(parser, Token_Type_Literal_String);
-                parser->parse_calls = 1;
-
-                export->export = parse_expression(parser, 0);
-
-                ENTITY_SUBMIT(export);
-                return;
-            }
-            else if (parse_possible_directive(parser, "thread_local")) {
-                OnyxToken* symbol = expect_token(parser, Token_Type_Symbol);
-                AstMemRes* memres = parse_memory_reservation(parser, symbol, 1);
-
-                binding = make_node(AstBinding, Ast_Kind_Binding);
-                binding->token = symbol;
-                binding->flags |= private_kind;
-                binding->node = (AstNode *) memres;
-
-                goto submit_binding_to_entities;
-            }
-            else if (parse_possible_directive(parser, "init")) {
-                // :LinearTokenDependent
-                parse_init_directive(parser, parser->curr - 2);
-                return;
-            }
-            else if (parse_possible_directive(parser, "library")) {
-                // :LinearTokenDependent
-                AstDirectiveLibrary *library = make_node(AstDirectiveLibrary, Ast_Kind_Directive_Library);
-                library->token = parser->curr - 2;
-                library->library_symbol = parse_expression(parser, 0);
-
-                ENTITY_SUBMIT(library);
-                return;
-            }
-            else if (parse_possible_directive(parser, "tag")) {
-                parser->tag_depth += 1;
-
-                AstTyped *expr = parse_expression(parser, 0);
-                bh_arr_push(parser->stored_tags, expr);
-
-                parser->tag_depth -= 1;
-                return;
-            }
-            else {
-                OnyxToken* directive_token = expect_token(parser, '#');
-                OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
-
-                onyx_report_error(directive_token->pos, Error_Critical, "unknown directive '#%b'.", symbol_token->text, symbol_token->length);
-                return;
-            }
-        }
-
-        default: break;
-    }
-
-    expect_token(parser, ';');
-    return;
-
-submit_binding_to_entities:
-    {
-        if (!binding) return;
-
-        Scope* target_scope = parser->package->scope;
-
-        if (binding->flags & Ast_Flag_Private_Package)
-            target_scope = parser->package->private_scope;
-        if (binding->flags & Ast_Flag_Private_File)
-            target_scope = parser->file_scope;
-
-        ENTITY_SUBMIT_IN_SCOPE(binding, target_scope);
-    }
-}
-
-static AstPackage* parse_package_expression(OnyxParser* parser) {
-    AstPackage* package_node = make_node(AstPackage, Ast_Kind_Package);
-    package_node->token = expect_token(parser, Token_Type_Keyword_Package);
-
-    bh_arr_new(global_heap_allocator, package_node->path, 2);
-
-    while (parser->curr->type == Token_Type_Symbol) {
-        if (parser->hit_unexpected_token) return package_node;
-
-        OnyxToken* symbol = expect_token(parser, Token_Type_Symbol);
-
-        bh_arr_push(package_node->path, symbol);
-
-        if (consume_token_if_next(parser, '.'));
-        else break;
-    }
-
-    i32 total_package_name_length = 0;
-    bh_arr_each(OnyxToken *, token, package_node->path) {
-        total_package_name_length += (*token)->length + 1;
-    }
-
-    char* package_name = bh_alloc_array(context.ast_alloc, char, total_package_name_length);
-    *package_name = '\0';
-
-    bh_arr_each(OnyxToken *, token, package_node->path) {
-        token_toggle_end(*token);
-        strncat(package_name, (*token)->text, total_package_name_length - 1);
-        token_toggle_end(*token);
-
-        if (token != &bh_arr_last(package_node->path)) {
-            strncat(package_name, ".", total_package_name_length - 1);
-        }
-    }
-
-    package_node->package_name = package_name;
-    package_node->package = package_lookup(package_name);
-
-    return package_node;
-}
-
-static Package* parse_file_package(OnyxParser* parser) {
-    if (parser->curr->type != Token_Type_Keyword_Package) {
-        return package_lookup_or_create("main", context.global_scope, parser->allocator, parser->curr->pos);
-    }
-
-    AstPackage* package_node = parse_package_expression(parser);
-
-    char aggregate_name[2048];
-    aggregate_name[0] = '\0';
-
-    Package* prevpackage = NULL;
-
-    bh_arr_each(OnyxToken *, symbol, package_node->path) {
-        token_toggle_end(*symbol);
-
-        strncat(aggregate_name, (*symbol)->text, 2047);
-        Package* newpackage = package_lookup_or_create(aggregate_name, context.global_scope, parser->allocator, package_node->token->pos);
-
-        AstPackage* pnode = make_node(AstPackage, Ast_Kind_Package);
-        pnode->token = *symbol;
-        pnode->package = newpackage;
-        pnode->package_name = newpackage->name;
-
-        if (prevpackage != NULL) {
-            symbol_subpackage_introduce(prevpackage->scope, (*symbol)->text, pnode);
-            package_reinsert_use_packages(prevpackage);
-        }
-
-        token_toggle_end(*symbol);
-        strncat(aggregate_name, ".", 2047);
-
-        prevpackage = newpackage;
-    }
-
-    package_node->package = prevpackage;
-
-    return package_node->package;
-}
-
-static void parse_top_level_statements_until(OnyxParser* parser, TokenType tt) {
-    while (parser->curr->type != tt) {
-        if (parser->hit_unexpected_token) break;
-        if (onyx_has_errors()) break;
-        parse_top_level_statement(parser);
-    }
-}
-
-
-// NOTE: This returns a void* so I don't need to cast it everytime I use it
-void* onyx_ast_node_new(bh_allocator alloc, i32 size, AstKind kind) {
-    void* node = bh_alloc(alloc, size);
-
-    memset(node, 0, size);
-    *(AstKind *) node = kind;
-
-    return node;
-}
-
-OnyxParser onyx_parser_create(bh_allocator alloc, OnyxTokenizer *tokenizer) {
-    OnyxParser parser;
-
-    parser.allocator = alloc;
-    parser.tokenizer = tokenizer;
-    parser.curr = tokenizer->tokens;
-    parser.prev = NULL;
-    parser.hit_unexpected_token = 0;
-    parser.current_scope = NULL;
-    parser.alternate_entity_placement_stack = NULL;
-    parser.current_symbol_stack = NULL;
-    parser.current_function_stack = NULL;
-    parser.scope_flags = NULL;
-    parser.stored_tags = NULL;
-    parser.parse_calls = 1;
-    parser.tag_depth = 0;
-
-    parser.polymorph_context = (PolymorphicContext) {
-        .root_node = NULL,
-        .poly_params = NULL,
-    };
-
-    bh_arr_new(global_heap_allocator, parser.alternate_entity_placement_stack, 4);
-    bh_arr_new(global_heap_allocator, parser.current_symbol_stack, 4);
-    bh_arr_new(global_heap_allocator, parser.scope_flags, 4);
-    bh_arr_new(global_heap_allocator, parser.stored_tags, 4);
-
-    return parser;
-}
-
-void onyx_parser_free(OnyxParser* parser) {
-}
-
-void onyx_parse(OnyxParser *parser) {
-    // NOTE: Skip comments at the beginning of the file
-    while (consume_token_if_next(parser, Token_Type_Comment) || consume_token_if_next(parser, Token_Type_Note));
-
-    parser->package = parse_file_package(parser);
-    parser->file_scope = scope_create(parser->allocator, parser->package->private_scope, parser->tokenizer->tokens[0].pos);
-    parser->current_scope = parser->file_scope;
-
-    parse_top_level_statements_until(parser, Token_Type_End_Stream);
-
-    parser->current_scope = parser->current_scope->parent;
-}
diff --git a/src/polymorph.h b/src/polymorph.h
deleted file mode 100644 (file)
index 0185b8f..0000000
+++ /dev/null
@@ -1,1091 +0,0 @@
-
-//
-// Polymorphic Procedures
-//
-
-// This flag is used by some of the procedures that try working with polymorphic things,
-// but need to wait until more information is known. Instead of passing a out parameter
-// into each of these procedures, a single global variable is used instead. If the type
-// checker ever gets multi-threaded, this would have to become a threadlocal variable.
-static b32 flag_to_yield = 0;
-
-// This flag is used in the very special case that you are passing a polymorphic procedure
-// to a polymorphic procedure, and you have enough information to instantiate said procedure
-// in order to resolve the type of one of the return values.
-static b32 doing_nested_polymorph_lookup = 0;
-
-// The name is pretty self-descriptive, but this is a node that is returned from things
-// like polymorphic_proc_lookup when it is determined that everything works so far, but
-// the caller must yield in order to finish checking this polymorphic procedure.
-AstTyped node_that_signals_a_yield = { Ast_Kind_Function, 0 };
-AstTyped node_that_signals_failure = { Ast_Kind_Error, 0 };
-
-static void ensure_polyproc_cache_is_created(AstFunction* pp) {
-    if (pp->concrete_funcs == NULL)        sh_new_arena(pp->concrete_funcs);
-    if (pp->active_queries.hashes == NULL) bh_imap_init(&pp->active_queries, global_heap_allocator, 31);
-}
-
-void insert_poly_sln_into_scope(Scope* scope, AstPolySolution *sln) {
-    AstNode *node = NULL;
-
-    switch (sln->kind) {
-        case PSK_Type:
-            node = onyx_ast_node_new(context.ast_alloc, sizeof(AstTypeRawAlias), Ast_Kind_Type_Raw_Alias);
-            ((AstTypeRawAlias *) node)->token = sln->poly_sym->token;
-            ((AstTypeRawAlias *) node)->to = sln->type;
-            ((AstTypeRawAlias *) node)->type = &basic_types[Basic_Kind_Type_Index];
-            ((AstTypeRawAlias *) node)->type_id = sln->type->id;
-            break;
-
-        case PSK_Value:
-            // CLEANUP: Maybe clone this?
-            assert(sln->value->flags & Ast_Flag_Comptime);
-            node = (AstNode *) sln->value;
-            break;
-    }
-
-    symbol_introduce(scope, sln->poly_sym->token, node);
-}
-
-static void insert_poly_slns_into_scope(Scope* scope, bh_arr(AstPolySolution) slns) {
-    bh_arr_each(AstPolySolution, sln, slns) {
-        insert_poly_sln_into_scope(scope, sln);
-    }
-}
-
-// NOTE: This might return a volatile string. Do not store it without copying it.
-static char* build_poly_solution_key(AstPolySolution* sln) {
-    if (sln->kind == PSK_Type) {
-        return (char *) type_get_unique_name(sln->type);
-
-    } else if (sln->kind == PSK_Value) {
-        static char buffer[256];
-
-        fori (i, 0, 256) buffer[i] = 0;
-
-        if (sln->value->kind == Ast_Kind_NumLit) {
-            strncat(buffer, "NUMLIT:", 127);
-            strncat(buffer, bh_bprintf("%l", ((AstNumLit *) sln->value)->value.l), 127);
-
-        } else {
-            // HACK: For now, the value pointer is just used. This means that
-            // sometimes, even through the solution is the same, it won't be
-            // stored the same.
-            bh_snprintf(buffer, 128, "%p", sln->value);
-        }
-
-        return buffer;
-    }
-
-    return NULL;
-}
-
-// NOTE: This returns a volatile string. Do not store it without copying it.
-static char* build_poly_slns_unique_key(bh_arr(AstPolySolution) slns) {
-    static char key_buf[1024];
-    fori (i, 0, 1024) key_buf[i] = 0;
-
-    bh_arr_each(AstPolySolution, sln, slns) {
-        token_toggle_end(sln->poly_sym->token);
-
-        strncat(key_buf, sln->poly_sym->token->text, 1023);
-        strncat(key_buf, "=", 1023);
-        strncat(key_buf, build_poly_solution_key(sln), 1023);
-        strncat(key_buf, ";", 1023);
-
-        token_toggle_end(sln->poly_sym->token);
-    }
-
-    return key_buf;
-}
-
-// NOTE: This function adds a solidified function to the entity heap for it to be processed
-// later. It optionally can start the function header entity at the code generation state if
-// the header has already been processed.
-static b32 add_solidified_function_entities(AstSolidifiedFunction *solidified_func) {
-    solidified_func->func->flags |= Ast_Flag_Function_Used;
-    solidified_func->func->flags |= Ast_Flag_From_Polymorphism;
-
-    Entity func_header_entity = {
-        .state = Entity_State_Resolve_Symbols,
-        .type = Entity_Type_Function_Header,
-        .function = solidified_func->func,
-        .package = NULL,
-        .scope = solidified_func->func->poly_scope,
-    };
-
-    Entity func_entity = {
-        .state = Entity_State_Resolve_Symbols,
-        .type = Entity_Type_Function,
-        .function = solidified_func->func,
-        .package = NULL,
-        .scope = solidified_func->func->poly_scope,
-    };
-
-    Entity* entity_header = entity_heap_insert(&context.entities, func_header_entity);
-    Entity* entity_body   = entity_heap_insert(&context.entities, func_entity);
-
-    solidified_func->func_header_entity  = entity_header;
-    solidified_func->func->entity_header = entity_header;
-    solidified_func->func->entity_body   = entity_body;
-    solidified_func->func->entity        = entity_body;
-    return 1;
-}
-
-// NOTE: This function is responsible for taking all of the information about generating
-// a new polymorphic variant, and producing a solidified function. It optionally can only
-// generate the header of the function, which is useful for cases such as checking if a
-// set of arguments works for a polymorphic overload option.
-static AstSolidifiedFunction generate_solidified_function(
-    AstFunction* pp,
-    bh_arr(AstPolySolution) slns,
-    OnyxToken* tkn,
-    b32 header_only) {
-
-    AstSolidifiedFunction solidified_func;
-    solidified_func.func_header_entity = NULL;
-
-    // NOTE: Use the position of token if one was provided, otherwise just use NULL.
-    OnyxFilePos poly_scope_pos = { 0 };
-    if (tkn) poly_scope_pos = tkn->pos;
-
-    if (header_only) {
-        solidified_func.func = (AstFunction *) clone_function_header(context.ast_alloc, pp);
-        solidified_func.func->flags |= Ast_Flag_Incomplete_Body;
-
-    } else {
-        solidified_func.func = (AstFunction *) ast_clone(context.ast_alloc, pp);
-    }
-
-    solidified_func.func->poly_scope = scope_create(context.ast_alloc, pp->parent_scope_of_poly_proc, poly_scope_pos);
-    insert_poly_slns_into_scope(solidified_func.func->poly_scope, slns);
-
-    solidified_func.func->flags |= Ast_Flag_From_Polymorphism;
-    solidified_func.func->generated_from = tkn;
-
-    // HACK: Remove the baked parameters from the function defintion so they can be
-    // resolved in the poly scope above the function. This does feel kinda of gross
-    // and I would love an alternative to tell it to just "skip" the parameter, but
-    // that is liable to breaking because it is one more thing to remember.
-    //                                             - brendanfh 2021/01/18
-    u32 removed_params = 0;
-    bh_arr_each(AstPolyParam, param, pp->poly_params) {
-        if (param->kind != PPK_Baked_Value) continue;
-
-        bh_arr_deleten(solidified_func.func->params, param->idx - removed_params, 1);
-        removed_params++;
-    }
-
-    return solidified_func;
-}
-
-static void ensure_solidified_function_has_body(AstFunction* pp, AstSolidifiedFunction *solidified_func) {
-    if (solidified_func->func->flags & Ast_Flag_Incomplete_Body) {
-        clone_function_body(context.ast_alloc, solidified_func->func, pp);
-
-        // HACK: I'm asserting that this function should return without an error, because
-        // the only case where it can return an error is if there was a problem with the
-        // header. This should never be the case in this situation, since the header would
-        // have to have successfully passed type checking before it would become a solidified
-        // procedure.
-        assert(add_solidified_function_entities(solidified_func));
-
-        solidified_func->func->flags &= ~Ast_Flag_Incomplete_Body;
-    }
-}
-
-// NOTE: These are temporary data structures used to represent the pattern matching system
-// of polymorphic type resolution.
-typedef struct PolySolveResult {
-    PolySolutionKind kind;
-    union {
-        AstTyped* value;
-        Type*     actual;
-    };
-} PolySolveResult;
-
-typedef struct PolySolveElem {
-    AstType* type_expr;
-
-    PolySolutionKind kind;
-    union {
-        AstTyped* value;
-        Type*     actual;
-    };
-} PolySolveElem;
-
-// NOTE: The job of this function is to solve for the type/value that belongs in a
-// polymorphic variable. This function takes in three arguments:
-//  * The symbol node of the polymorphic parameter being searched for
-//  * The type expression that should contain the symbol node it is some where
-//  * The actual type to pattern match against
-//
-// This function utilizes a basic breadth-first search of the type_expr and actual type
-// trees, always moving along them in parallel, so when the target is reached (if it is
-// ever reached), the "actual" is the matched type/value.
-static PolySolveResult solve_poly_type(AstNode* target, AstType* type_expr, Type* actual) {
-    bh_arr(PolySolveElem) elem_queue = NULL;
-    bh_arr_new(global_heap_allocator, elem_queue, 4);
-
-    PolySolveResult result = { PSK_Undefined, { NULL } };
-
-    bh_arr_push(elem_queue, ((PolySolveElem) {
-        .type_expr = type_expr,
-        .kind = PSK_Type,
-        .actual    = actual
-    }));
-
-    while (!bh_arr_is_empty(elem_queue)) {
-        PolySolveElem elem = elem_queue[0];
-        bh_arr_deleten(elem_queue, 0, 1);
-
-        if (elem.type_expr == (AstType *) target) {
-            result.kind = elem.kind;
-
-            assert(elem.kind != PSK_Undefined);
-            if (result.kind == PSK_Type)  result.actual = elem.actual;
-            if (result.kind == PSK_Value) result.value = elem.value;
-            break;
-        }
-
-        if (elem.kind != PSK_Type) continue;
-
-        switch (elem.type_expr->kind) {
-            case Ast_Kind_Pointer_Type: {
-                if (elem.actual->kind != Type_Kind_Pointer) break;
-
-                bh_arr_push(elem_queue, ((PolySolveElem) {
-                    .type_expr = ((AstPointerType *) elem.type_expr)->elem,
-                    .kind = PSK_Type,
-                    .actual = elem.actual->Pointer.elem,
-                }));
-                break;
-            }
-
-            case Ast_Kind_Address_Of: {
-                if (elem.actual->kind != Type_Kind_Pointer) break;
-
-                bh_arr_push(elem_queue, ((PolySolveElem) {
-                    .type_expr = (AstType *) ((AstAddressOf *) elem.type_expr)->expr,
-                    .kind = PSK_Type,
-                    .actual = elem.actual->Pointer.elem,
-                }));
-                break;
-            }
-
-            case Ast_Kind_Array_Type: {
-                if (elem.actual->kind != Type_Kind_Array) break;
-
-                bh_arr_push(elem_queue, ((PolySolveElem) {
-                    .type_expr = (AstType*) ((AstArrayType *) elem.type_expr)->count_expr,
-                    .kind = PSK_Value,
-
-                    // CLEANUP: Making an integer literal every time is very very very gross. This should
-                    // at least be cached or something.
-                    .value = (AstTyped *) make_int_literal(context.ast_alloc, elem.actual->Array.count)
-                }));
-
-                bh_arr_push(elem_queue, ((PolySolveElem) {
-                    .type_expr = ((AstArrayType *) elem.type_expr)->elem,
-                    .kind = PSK_Type,
-                    .actual = elem.actual->Array.elem,
-                }));
-                break;
-            }
-
-            case Ast_Kind_Slice_Type: {
-                if (elem.actual->kind != Type_Kind_Slice && elem.actual->kind != Type_Kind_DynArray
-                        && elem.actual->kind != Type_Kind_VarArgs && elem.actual->kind != Type_Kind_Array) break;
-
-                bh_arr_push(elem_queue, ((PolySolveElem) {
-                    .type_expr = ((AstSliceType *) elem.type_expr)->elem,
-                    .kind = PSK_Type,
-
-                    // HACK: This makes the assumption that arrays, slices, dynamic arrays and varargs have the same element type at the same location.
-                    .actual = elem.actual->Slice.elem,
-                }));
-                break;
-            }
-
-            case Ast_Kind_DynArr_Type: {
-                if (elem.actual->kind != Type_Kind_DynArray) break;
-
-                bh_arr_push(elem_queue, ((PolySolveElem) {
-                    .type_expr = ((AstDynArrType *) elem.type_expr)->elem,
-                    .kind = PSK_Type,
-                    .actual = elem.actual->DynArray.elem,
-                }));
-                break;
-            }
-
-            case Ast_Kind_VarArg_Type:
-                bh_arr_push(elem_queue, ((PolySolveElem) {
-                    .type_expr = ((AstVarArgType *) elem.type_expr)->elem,
-                    .kind = PSK_Type,
-                    .actual = actual,
-                }));
-                break;
-
-            case Ast_Kind_Function_Type: {
-                if (elem.actual->kind != Type_Kind_Function) break;
-
-                AstFunctionType* ft = (AstFunctionType *) elem.type_expr;
-
-                fori (i, 0, (i64) ft->param_count) {
-                    bh_arr_push(elem_queue, ((PolySolveElem) {
-                        .type_expr = ft->params[i],
-                        .kind = PSK_Type,
-                        .actual = elem.actual->Function.params[i],
-                    }));
-                }
-
-                bh_arr_push(elem_queue, ((PolySolveElem) {
-                    .type_expr = ft->return_type,
-                    .kind = PSK_Type,
-                    .actual = elem.actual->Function.return_type,
-                }));
-
-                break;
-            }
-
-            case Ast_Kind_Call: {
-                AstPolyCallType *pct = convert_call_to_polycall((AstCall *) elem.type_expr);
-                elem.type_expr = (AstType *) pct;
-
-                // fallthrough
-            }
-
-            case Ast_Kind_Poly_Call_Type: {
-                if (elem.actual->kind != Type_Kind_Struct) break;
-                if (bh_arr_length(elem.actual->Struct.poly_sln) != bh_arr_length(((AstPolyCallType *) elem.type_expr)->params)) break;
-
-                AstPolyCallType* pt = (AstPolyCallType *) elem.type_expr;
-
-                fori (i, 0, bh_arr_length(pt->params)) {
-                    PolySolutionKind kind = elem.actual->Struct.poly_sln[i].kind;
-                    if (kind == PSK_Type) {
-                        bh_arr_push(elem_queue, ((PolySolveElem) {
-                            .kind = kind,
-                            .type_expr = (AstType *) pt->params[i],
-                            .actual = elem.actual->Struct.poly_sln[i].type,
-                        }));
-                    } else {
-                        bh_arr_push(elem_queue, ((PolySolveElem) {
-                            .kind = kind,
-                            .type_expr = (AstType *) pt->params[i],
-                            .value = elem.actual->Struct.poly_sln[i].value,
-                        }));
-                    }
-                }
-
-                break;
-            }
-
-            case Ast_Kind_Type_Compound: {
-                if (elem.actual->kind != Type_Kind_Compound) break;
-                if (elem.actual->Compound.count != (u32) bh_arr_length(((AstCompoundType *) elem.type_expr)->types)) break;
-
-                AstCompoundType* ct = (AstCompoundType *) elem.type_expr;
-
-                fori (i, 0, bh_arr_length(ct->types)) {
-                    bh_arr_push(elem_queue, ((PolySolveElem) {
-                        .kind = PSK_Type,
-                        .type_expr = ct->types[i],
-                        .actual = elem.actual->Compound.types[i],
-                    }));
-                }
-
-                break;
-            }
-
-            default: break;
-        }
-    }
-
-    bh_arr_free(elem_queue);
-
-    return result;
-}
-
-// NOTE: The job of this function is to take a polymorphic parameter and a set of arguments
-// and solve for the argument that matches the parameter. This is needed because polymorphic
-// procedure resolution has to happen before the named arguments are placed in their correct
-// positions.
-static AstTyped* lookup_param_in_arguments(AstFunction* func, AstPolyParam* param, Arguments* args, char** err_msg) {
-    bh_arr(AstTyped *) arg_arr = args->values;
-    bh_arr(AstNamedValue *) named_values = args->named_values;
-
-    if ((i32) param->idx < 0)
-        return NULL;
-
-    // NOTE: This check is safe because currently the arguments given without a name
-    // always map to the beginning indidies of the argument array.
-    if (param->idx >= (u64) bh_arr_length(arg_arr)) {
-        OnyxToken* param_name = func->params[param->idx].local->token;
-
-        bh_arr_each(AstNamedValue *, named_value, named_values) {
-            if (token_equals(param_name, (*named_value)->token)) {
-                return (AstTyped *) (*named_value)->value;
-            }
-        }
-
-        if (param->idx <= (u64) bh_arr_length(func->params)) {
-            if (func->params[param->idx].default_value) {
-                return (AstTyped *) func->params[param->idx].default_value;
-            }
-        }
-
-        // CLEANUP
-        if (err_msg) *err_msg = "Not enough arguments to polymorphic procedure. This error message may not be entirely right.";
-
-    } else {
-        return (AstTyped *) arg_arr[param->idx];
-    }
-
-    return NULL;
-}
-
-static AstTyped* try_lookup_based_on_partial_function_type(AstFunction *pp, AstFunctionType *ft) {
-    if (ft->partial_function_type == NULL) {
-        AstType *old_return_type = ft->return_type;
-        ft->return_type = (AstType *) &basic_type_void;
-        ft->partial_function_type = type_build_from_ast(context.ast_alloc, (AstType *) ft);
-        ft->return_type = old_return_type;
-        if (!ft->partial_function_type) {
-            doing_nested_polymorph_lookup = 1;
-            return NULL;
-        }
-
-        assert(ft->partial_function_type);
-    }
-
-    AstTyped *result = (AstTyped *) polymorphic_proc_lookup(pp, PPLM_By_Function_Type, ft->partial_function_type, pp->token);
-    if (result && result->type == NULL) {
-        doing_nested_polymorph_lookup = 1;
-        result = NULL;
-    }
-    if (result == &node_that_signals_a_yield) {
-        doing_nested_polymorph_lookup = 1;
-        result = NULL;
-    }
-
-    return result;
-}
-
-// NOTE: The job of this function is to solve for type of AstPolySolution using the provided
-// information. It is asssumed that the "param" is of kind PPK_Poly_Type. This function uses
-// either the arguments provided, or a function type to compare against to pattern match for
-// the type that the parameter but be.
-static void solve_for_polymorphic_param_type(PolySolveResult* resolved, AstFunction* func, AstPolyParam* param, PolyProcLookupMethod pp_lookup, ptr actual, char** err_msg) {
-    Type* actual_type = NULL;
-
-    switch (pp_lookup) {
-        case PPLM_By_Arguments: {
-            Arguments* args = (Arguments *) actual;
-
-            AstTyped* typed_param = lookup_param_in_arguments(func, param, args, err_msg);
-            if (typed_param == NULL) return;
-
-            // CLEANUP FIXME HACK TODO GROSS
-            if (typed_param->kind == Ast_Kind_Argument) {
-                AstTyped* potential = ((AstArgument *) typed_param)->value;
-                if (potential->kind == Ast_Kind_Polymorphic_Proc) {
-                    if (param->idx < (u32) bh_arr_length(func->params)) {
-                        AstType *param_type = func->params[param->idx].local->type_node;
-                        if (param_type->kind == Ast_Kind_Function_Type) {
-                            AstFunctionType *ft = (AstFunctionType *) param_type;
-                            b32 all_types = 1;
-                            fori (i, 0, (i32) ft->param_count) {
-                                if (!node_is_type((AstNode *) ft->params[i])) {
-                                    all_types = 0;
-                                    break;
-                                }
-                            }
-
-                            if (all_types) {
-                                typed_param = try_lookup_based_on_partial_function_type((AstFunction *) potential, ft);
-                            }
-                        }
-                    }
-                }
-            }
-
-            actual_type = resolve_expression_type(typed_param);
-            if (actual_type == NULL) return;
-
-            break;
-        }
-
-        case PPLM_By_Function_Type: {
-            Type* ft = (Type *) actual;
-            if (param->idx >= ft->Function.param_count) {
-                if (err_msg) *err_msg = "Incompatible polymorphic argument to function parameter.";
-                return;
-            }
-
-            actual_type = ft->Function.params[param->idx];
-            break;
-        }
-
-        default: return;
-    }
-
-    PolySolveResult res = solve_poly_type(param->poly_sym, param->type_expr, actual_type);
-    if (res.kind == PSK_Undefined) {
-        *err_msg = bh_aprintf(global_scratch_allocator,
-            "Unable to solve for polymorphic variable '%b', given the type '%s'.",
-            param->poly_sym->token->text,
-            param->poly_sym->token->length,
-            type_get_name(actual_type));
-    }
-
-    *resolved = res;
-}
-
-
-// NOTE: The job of this function is to look through the arguments provided and find a matching
-// value that is to be baked into the polymorphic procedures poly-scope. It expected that param
-// will be of kind PPK_Baked_Value. In other words, this handles the ($Baked: type) case.
-// CLEANUP: This function is kind of gross at the moment, because it handles different cases for
-// the argument kind. When type expressions (type_expr) become first-class types in the type
-// system, this code should be able to be a lot cleaner.
-static void solve_for_polymorphic_param_value(PolySolveResult* resolved, AstFunction* func, AstPolyParam* param, PolyProcLookupMethod pp_lookup, ptr actual, char** err_msg) {
-    if (pp_lookup != PPLM_By_Arguments) {
-        *err_msg = "Function type cannot be used to solved for baked parameter value.";
-        return;
-    }
-
-    Arguments* args = (Arguments *) actual;
-    AstTyped* value = lookup_param_in_arguments(func, param, args, err_msg);
-    if (value == NULL) return;
-
-    // HACK: Storing the original value because if this was an AstArgument, we need to flag
-    // it as baked if it is determined that the argument is of the correct kind and type.
-    AstTyped* orig_value = value;
-    if (value->kind == Ast_Kind_Argument) {
-        ((AstArgument *) orig_value)->is_baked = 0;
-        value = ((AstArgument *) value)->value;
-    }
-
-    Type*    param_type = NULL;
-    AstType *param_type_expr = func->params[param->idx].local->type_node;
-    if (param_type_expr == (AstType *) &basic_type_type_expr) {
-        if (!node_is_type((AstNode *) value)) {
-            if (err_msg) *err_msg = "Expected type expression.";
-            return;
-        }
-
-        Type* resolved_type = type_build_from_ast(context.ast_alloc, (AstType *) value);
-        if (resolved_type == NULL) flag_to_yield = 1;
-
-        *resolved = ((PolySolveResult) { PSK_Type, .actual = resolved_type });
-
-    } else {
-        resolve_expression_type(value);
-
-        if ((value->flags & Ast_Flag_Comptime) == 0) {
-            if (err_msg) *err_msg = "Expected compile-time known argument.";
-            return;
-        }
-
-        param_type = type_build_from_ast(context.ast_alloc, param_type_expr);
-        if (param_type == NULL) {
-            flag_to_yield = 1;
-            *err_msg = "Waiting to know type for polymorphic value.";
-            return;
-        }
-
-        AstTyped* value_to_use = value;
-        if (value->kind == Ast_Kind_Macro) {
-            value_to_use = (AstTyped *) get_function_from_node((AstNode *) value);
-        }
-
-        TypeMatch tm = unify_node_and_type(&value_to_use, param_type);
-        if (tm == TYPE_MATCH_FAILED) {
-            if (err_msg) *err_msg = bh_aprintf(global_scratch_allocator,
-                    "The procedure '%s' expects a value of type '%s' for baked %d%s parameter, got '%s'.",
-                    get_function_name(func),
-                    type_get_name(param_type),
-                    param->idx + 1,
-                    bh_num_suffix(param->idx + 1),
-                    node_get_type_name(value_to_use));
-            return;
-        }
-
-        if (tm == TYPE_MATCH_YIELD) flag_to_yield = 1;
-
-        *resolved = ((PolySolveResult) { PSK_Value, value });
-    }
-
-    if (orig_value->kind == Ast_Kind_Argument) {
-        ((AstArgument *) orig_value)->is_baked = 1;
-    }
-}
-
-TypeMatch find_polymorphic_sln(AstPolySolution *out, AstPolyParam *param, AstFunction *func, PolyProcLookupMethod pp_lookup, ptr actual, char** err_msg) {
-    // NOTE: Solve for the polymorphic parameter's value
-    PolySolveResult resolved = { PSK_Undefined };
-    switch (param->kind) {
-        case PPK_Poly_Type:   solve_for_polymorphic_param_type (&resolved, func, param, pp_lookup, actual, err_msg); break;
-        case PPK_Baked_Value: solve_for_polymorphic_param_value(&resolved, func, param, pp_lookup, actual, err_msg); break;
-
-        default: if (err_msg) *err_msg = "Invalid polymorphic parameter kind. This is a compiler bug.";
-    }
-
-    if (doing_nested_polymorph_lookup) {
-        doing_nested_polymorph_lookup = 0;
-        return TYPE_MATCH_SPECIAL;
-    }
-
-    if (flag_to_yield) {
-        flag_to_yield = 0;
-        return TYPE_MATCH_YIELD;
-    }
-
-    switch (resolved.kind) {
-        case PSK_Type:
-            out->kind = PSK_Type;
-            out->poly_sym = param->poly_sym;
-            out->type = resolved.actual;
-            return TYPE_MATCH_SUCCESS;
-
-        case PSK_Value:
-            out->kind = PSK_Value;
-            out->poly_sym = param->poly_sym;
-            out->value = resolved.value;
-            return TYPE_MATCH_SUCCESS;
-
-        case PSK_Undefined:
-        default:
-            // NOTE: If no error message has been assigned to why this polymorphic parameter
-            // resolution was unsuccessful, provide a basic dummy one.
-            if (err_msg && *err_msg == NULL)
-                *err_msg = bh_aprintf(global_scratch_allocator,
-                    "Unable to solve for polymorphic variable '%b'.",
-                    param->poly_sym->token->text,
-                    param->poly_sym->token->length);
-
-            out->kind = PSK_Undefined;
-            return TYPE_MATCH_FAILED;
-    }
-}
-
-// NOTE: The job of this function is to take a polymorphic procedure, as well as a method of
-// solving for the polymorphic variables, in order to return an array of the solutions for all
-// of the polymorphic variables.
-static bh_arr(AstPolySolution) find_polymorphic_slns(AstFunction* pp, PolyProcLookupMethod pp_lookup, ptr actual, OnyxToken *tkn, b32 necessary) {
-    ensure_polyproc_cache_is_created(pp);
-    if (bh_imap_has(&pp->active_queries, (u64) actual)) {
-        AstPolyQuery *query = (AstPolyQuery *) bh_imap_get(&pp->active_queries, (u64) actual);
-        assert(query->kind == Ast_Kind_Polymorph_Query);
-        assert(query->entity);
-
-        if (query->entity->state == Entity_State_Finalized) return query->slns;
-        if (query->entity->state == Entity_State_Failed)    return NULL;
-
-        flag_to_yield = 1;
-        return NULL;
-    }
-
-    bh_arr(AstPolySolution) slns = NULL;
-    bh_arr_new(global_heap_allocator, slns, bh_arr_length(pp->poly_params));
-
-    // NOTE: "known solutions" are given through a '#solidify' directive. If this polymorphic
-    // procedure is the result of a partially applied solidification, this array will be non-
-    // empty and these solutions will be used.
-    bh_arr_each(AstPolySolution, known_sln, pp->known_slns) bh_arr_push(slns, *known_sln);
-
-    AstPolyQuery *query = onyx_ast_node_new(context.ast_alloc, sizeof(AstPolyQuery), Ast_Kind_Polymorph_Query);
-    query->token = pp->token;
-    query->proc = pp;
-    query->pp_lookup = pp_lookup;
-    query->given = actual;
-    query->error_loc = tkn;
-    query->slns = slns;
-    query->function_header = clone_function_header(context.ast_alloc, pp);
-    query->function_header->flags |= Ast_Flag_Header_Check_No_Error;
-    query->function_header->scope = NULL;
-    query->error_on_fail = necessary;
-    query->successful_symres = 1;
-
-    bh_imap_put(&pp->active_queries, (u64) actual, (u64) query);
-    add_entities_for_node(NULL, (AstNode *) query, NULL, NULL);
-
-    flag_to_yield = 1;
-    return NULL;
-}
-
-// NOTE: The job of this function is to be a wrapper to other functions, providing an error
-// message if a solution could not be found. This can't be merged with polymorphic_proc_solidify
-// because polymorphic_proc_try_solidify uses the aforementioned function.
-AstFunction* polymorphic_proc_lookup(AstFunction* pp, PolyProcLookupMethod pp_lookup, ptr actual, OnyxToken* tkn) {
-    ensure_polyproc_cache_is_created(pp);
-
-    bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, pp_lookup, actual, tkn, 1);
-    if (slns == NULL) {
-        if (flag_to_yield) {
-            flag_to_yield = 0;
-            return (AstFunction *) &node_that_signals_a_yield;
-        }
-
-        return NULL;
-    }
-
-    AstFunction* result = polymorphic_proc_solidify(pp, slns, tkn);
-    return result;
-}
-
-AstFunction* polymorphic_proc_solidify(AstFunction* pp, bh_arr(AstPolySolution) slns, OnyxToken* tkn) {
-    ensure_polyproc_cache_is_created(pp);
-
-    // NOTE: Check if a version of this polyproc has already been created.
-    char* unique_key = build_poly_slns_unique_key(slns);
-    i32 index = shgeti(pp->concrete_funcs, unique_key);
-    if (index != -1) {
-        AstSolidifiedFunction solidified_func = pp->concrete_funcs[index].value;
-
-        // NOTE: If this solution was originally created from a "build_only_header" call, then the body
-        // will not have been or type checked, or anything. This ensures that the body is copied, the
-        // entities are created and entered into the pipeline.
-        ensure_solidified_function_has_body(pp, &solidified_func);
-
-        // NOTE: Again, if this came from a "build_only_header" call, then there was no known token and
-        // the "generated_from" member will be null. It is best to set it here so errors reported in that
-        // function can report where the polymorphic instantiation occurred.
-        if (solidified_func.func->generated_from == NULL)
-            solidified_func.func->generated_from = tkn;
-
-        return solidified_func.func;
-    }
-
-    AstSolidifiedFunction solidified_func = generate_solidified_function(pp, slns, tkn, 0);
-    add_solidified_function_entities(&solidified_func);
-
-    // NOTE: Cache the function for later use, reducing duplicate functions.
-    shput(pp->concrete_funcs, unique_key, solidified_func);
-
-    return (AstFunction *) &node_that_signals_a_yield;
-}
-
-// NOTE: This can return either a AstFunction or an AstFunction, depending if enough parameters were
-// supplied to remove all the polymorphic variables from the function.
-AstNode* polymorphic_proc_try_solidify(AstFunction* pp, bh_arr(AstPolySolution) slns, OnyxToken* tkn) {
-    i32 valid_argument_count = 0;
-
-    bh_arr_each(AstPolySolution, sln, slns) {
-        b32 found_match = 0;
-
-        bh_arr_each(AstPolyParam, param, pp->poly_params) {
-            if (token_equals(sln->poly_sym->token, param->poly_sym->token)) {
-                found_match = 1;
-                break;
-            }
-        }
-
-        if (found_match) {
-            valid_argument_count++;
-        } else {
-            if (pp->name) {
-                onyx_report_error(tkn->pos, Error_Critical, "'%b' is not a type variable of '%s'.",
-                    sln->poly_sym->token->text, sln->poly_sym->token->length, pp->name);
-            } else {
-                onyx_report_error(tkn->pos, Error_Critical, "'%b' is not a type variable of '%b'.",
-                    sln->poly_sym->token->text, sln->poly_sym->token->length,
-                    pp->token->text, pp->token->length);
-            }
-            return (AstNode *) pp;
-        }
-    }
-
-    if (valid_argument_count == bh_arr_length(pp->poly_params)) {
-        return (AstNode *) polymorphic_proc_solidify(pp, slns, tkn);
-
-    } else {
-        // HACK: Some of these initializations assume that the entity for this polyproc has
-        // made it through the symbol resolution phase.
-        //                                                    - brendanfh 2020/12/25
-        AstFunction* new_pp = onyx_ast_node_new(context.ast_alloc, sizeof(AstFunction), Ast_Kind_Polymorphic_Proc);
-        memcpy(new_pp, pp, sizeof(AstFunction));
-        new_pp->token = tkn;
-        new_pp->poly_params = bh_arr_copy(context.ast_alloc, pp->poly_params);
-
-        ensure_polyproc_cache_is_created(pp);
-        new_pp->concrete_funcs = pp->concrete_funcs;
-
-        new_pp->known_slns = NULL;
-        bh_arr_new(global_heap_allocator, new_pp->known_slns, bh_arr_length(pp->known_slns) + bh_arr_length(slns));
-
-        bh_arr_each(AstPolySolution, sln, pp->known_slns) bh_arr_push(new_pp->known_slns, *sln);
-        bh_arr_each(AstPolySolution, sln, slns)           bh_arr_push(new_pp->known_slns, *sln);
-
-        return (AstNode *) new_pp;
-    }
-}
-
-AstFunction* polymorphic_proc_build_only_header(AstFunction* pp, PolyProcLookupMethod pp_lookup, ptr actual) {
-    ensure_polyproc_cache_is_created(pp);
-    bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, pp_lookup, actual, NULL, 0);
-    if (flag_to_yield) {
-        flag_to_yield = 0;
-        return (AstFunction *) &node_that_signals_a_yield;
-    }
-    if (slns == NULL) return NULL;
-
-    ensure_polyproc_cache_is_created(pp);
-
-    return polymorphic_proc_build_only_header_with_slns(pp, slns, 0);
-}
-
-AstFunction* polymorphic_proc_build_only_header_with_slns(AstFunction* pp, bh_arr(AstPolySolution) slns, b32 error_if_failed) {
-    AstSolidifiedFunction solidified_func;
-
-    char* unique_key = build_poly_slns_unique_key(slns);
-    i32 index = shgeti(pp->concrete_funcs, unique_key);
-    if (index != -1) {
-        solidified_func = pp->concrete_funcs[index].value;
-
-    } else {
-        // NOTE: This function is only going to have the header of it correctly created.
-        // Nothing should happen to this function's body or else the original will be corrupted.
-        //                                                      - brendanfh 2021/01/10
-        solidified_func = generate_solidified_function(pp, slns, NULL, 1);
-    }
-
-    if (solidified_func.func_header_entity) {
-        if (solidified_func.func_header_entity->state == Entity_State_Finalized) return solidified_func.func;
-        if (solidified_func.func_header_entity->state == Entity_State_Failed)    return NULL;
-
-        return (AstFunction *) &node_that_signals_a_yield;
-    }
-
-    BH_MASK_SET(solidified_func.func->flags, !error_if_failed, Ast_Flag_Header_Check_No_Error);
-
-    Entity func_header_entity = {
-        .state = Entity_State_Resolve_Symbols,
-        .type = Entity_Type_Temp_Function_Header,
-        .function = solidified_func.func,
-        .package = NULL,
-        .scope = solidified_func.func->poly_scope,
-    };
-
-    Entity* func_header_entity_ptr = entity_heap_insert(&context.entities, func_header_entity);
-    solidified_func.func_header_entity = func_header_entity_ptr;
-
-    // NOTE: Cache the function for later use.
-    shput(pp->concrete_funcs, unique_key, solidified_func);
-    
-    return (AstFunction *) &node_that_signals_a_yield;
-}
-
-typedef struct AutoPolymorphVariable {
-    u32 idx;
-    u32 variable_count;
-    AstType *base_type;
-    AstType **replace;
-} AutoPolymorphVariable;
-
-// This should be called after all the parameter types have been symresed, but before anything
-// happens to the body.
-b32 potentially_convert_function_to_polyproc(AstFunction *func) {
-    bh_arr(AutoPolymorphVariable) auto_vars = NULL;
-    bh_arr_new(global_heap_allocator, auto_vars, 2);
-
-    u32 param_idx = 0;
-    bh_arr_each(AstParam, param, func->params) {
-        AstType **to_replace = &param->local->type_node;
-        AstType *param_type = param->local->type_node;
-
-        b32 done = 0;
-        while (!done && param_type) {
-            switch (param_type->kind) {
-                case Ast_Kind_Pointer_Type: to_replace = &((AstPointerType *) *to_replace)->elem;  param_type = ((AstPointerType *) param_type)->elem;  break;
-                case Ast_Kind_Array_Type:   to_replace = &((AstArrayType *)   *to_replace)->elem;  param_type = ((AstArrayType *)   param_type)->elem;  break;
-                case Ast_Kind_Slice_Type:   to_replace = &((AstSliceType *)   *to_replace)->elem;  param_type = ((AstSliceType *)   param_type)->elem;  break;
-                case Ast_Kind_DynArr_Type:  to_replace = &((AstDynArrType *)  *to_replace)->elem;  param_type = ((AstDynArrType *)  param_type)->elem;  break;
-                case Ast_Kind_Alias:                                                               param_type = (AstType *) ((AstAlias *) param_type)->alias; break;
-                case Ast_Kind_Type_Alias:                                                          param_type = ((AstTypeAlias *)   param_type)->to;    break;
-                case Ast_Kind_Poly_Struct_Type: {
-                    AutoPolymorphVariable apv;
-                    apv.idx = param_idx;
-                    apv.base_type = param->local->type_node;
-                    apv.variable_count = bh_arr_length(((AstPolyStructType *) param_type)->poly_params);
-                    apv.replace = to_replace;
-
-                    bh_arr_push(auto_vars, apv);
-                    done = 1;
-                    break;
-                }
-
-                default: done = 1; break;
-            }
-        }
-
-        param_idx++;
-    }
-
-    if (bh_arr_length(auto_vars) == 0) return 0;
-
-    param_idx = 0;
-    bh_arr_each(AutoPolymorphVariable, apv, auto_vars) {
-        AstPolyParam pp;
-        pp.idx = apv->idx;
-        pp.kind = PPK_Poly_Type;
-
-        AstPolyCallType* pcall = onyx_ast_node_new(context.ast_alloc, sizeof(AstPolyCallType), Ast_Kind_Poly_Call_Type);
-        pcall->callee = *apv->replace;
-        pcall->token = pcall->callee->token;
-        bh_arr_new(global_heap_allocator, pcall->params, apv->variable_count);
-
-        if (apv->base_type->kind == Ast_Kind_Poly_Struct_Type) {
-            pp.type_expr = (AstType *) pcall;
-        } else {
-            pp.type_expr = apv->base_type;
-        }
-        *apv->replace = (AstType *) pcall;
-
-        fori (i, 0, apv->variable_count) {
-            OnyxToken* name_token = bh_alloc_item(context.ast_alloc, OnyxToken);
-            name_token->text = bh_aprintf(context.ast_alloc, "__autopoly_var_%d\0", param_idx);
-            name_token->length = strlen(name_token->text);
-            name_token->type = Token_Type_Symbol;
-            name_token->pos  = pcall->token->pos;
-
-            pp.poly_sym = make_symbol(context.ast_alloc, name_token);
-            bh_arr_push(pcall->params, pp.poly_sym);
-            bh_arr_push(func->poly_params, pp);
-            param_idx ++;
-        }
-    }
-
-    convert_function_to_polyproc(func);
-
-    bh_arr_each(AstParam, param, func->params) {
-        param->local->flags |= Ast_Flag_Param_Symbol_Dirty;
-    }
-
-    return 1;
-}
-
-//
-// Polymorphic Structures
-//
-//
-// Currently, I am not very happy about how polymorphic structure generation works. My biggest problem
-// with it is that it is very different from the polymorhic procedure generation. Also, it needs to
-// completely generate and check the structure right away, which means there is a lot of up-front work
-// done here that could probably be done elsewhere. This really relates to a large problem in the compiler
-// that types need to be known completely by the time symbol resolution is done, even though that
-// information shouldn't need to be known until right before the types are checked.
-//
-// The above documentation is very incorrect but I don't want to fix it right now. Basically, polymorphic
-// structures now have a delay instantiation phase and are not forced to be completed immediately.
-
-char* build_poly_struct_name(AstPolyStructType* ps_type, Type* cs_type) {
-    char name_buf[256];
-    fori (i, 0, 256) name_buf[i] = 0;
-
-    strncat(name_buf, ps_type->name, 255);
-    strncat(name_buf, "(", 255);
-    bh_arr_each(AstPolySolution, ptype, cs_type->Struct.poly_sln) {
-        if (ptype != cs_type->Struct.poly_sln)
-            strncat(name_buf, ", ", 255);
-
-        // This logic will have to be other places as well.
-
-        switch (ptype->kind) {
-            case PSK_Undefined: assert(0); break;
-            case PSK_Type:      strncat(name_buf, type_get_name(ptype->type), 255); break;
-            case PSK_Value: {
-                // FIX
-                AstNode* value = strip_aliases((AstNode *) ptype->value);
-
-                if (value->kind == Ast_Kind_NumLit) {
-                    AstNumLit* nl = (AstNumLit *) value;
-                    if (type_is_integer(nl->type)) {
-                        strncat(name_buf, bh_bprintf("%l", nl->value.l), 127);
-                    } else {
-                        strncat(name_buf, "numlit (FIX ME)", 127);
-                    }
-                } else if (value->kind == Ast_Kind_Code_Block) {
-                    AstCodeBlock* code = (AstCodeBlock *) value;
-                    OnyxFilePos code_loc = code->token->pos;
-                    strncat(name_buf, bh_bprintf("code at %s:%d,%d", code_loc.filename, code_loc.line, code_loc.column), 127);
-                } else {
-                    strncat(name_buf, "<expr>", 127);
-                }
-
-                break;
-            }
-        }
-    }
-    strncat(name_buf, ")", 255);
-
-    return bh_aprintf(global_heap_allocator, "%s", name_buf);
-}
-
-Type* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(AstPolySolution) slns, OnyxFilePos pos, b32 error_if_failed) {
-    // @Cleanup
-    assert(ps_type->scope != NULL);
-
-    if (ps_type->concrete_structs == NULL) {
-        sh_new_arena(ps_type->concrete_structs);
-    }
-
-    if (bh_arr_length(slns) != bh_arr_length(ps_type->poly_params)) {
-        onyx_report_error(pos, Error_Critical, "Wrong number of arguments for '%s'. Expected %d, got %d.",
-            ps_type->name,
-            bh_arr_length(ps_type->poly_params),
-            bh_arr_length(slns));
-
-        return NULL;
-    }
-
-    i32 i = 0;
-    bh_arr_each(AstPolySolution, sln, slns) {
-        sln->poly_sym = (AstNode *) &ps_type->poly_params[i];
-        i++;
-    }
-
-    char* unique_key = build_poly_slns_unique_key(slns);
-    i32 index = shgeti(ps_type->concrete_structs, unique_key);
-    if (index != -1) {
-        AstStructType* concrete_struct = ps_type->concrete_structs[index].value;
-
-        if (concrete_struct->entity_type->state < Entity_State_Check_Types) {
-            return NULL;
-        }
-
-        if (concrete_struct->entity_type->state == Entity_State_Failed) {
-            return (Type *) &node_that_signals_failure;
-        }
-
-        Type* cs_type = type_build_from_ast(context.ast_alloc, (AstType *) concrete_struct);
-        if (!cs_type) return NULL;
-
-        if (cs_type->Struct.poly_sln == NULL) cs_type->Struct.poly_sln = bh_arr_copy(global_heap_allocator, slns);
-        if (cs_type->Struct.name == NULL)     cs_type->Struct.name = build_poly_struct_name(ps_type, cs_type);
-
-        return cs_type;
-    }
-
-    Scope* sln_scope = scope_create(context.ast_alloc, ps_type->scope, ps_type->token->pos);
-    insert_poly_slns_into_scope(sln_scope, slns);
-
-    AstStructType* concrete_struct = (AstStructType *) ast_clone(context.ast_alloc, ps_type->base_struct);
-    concrete_struct->polymorphic_error_loc = pos;
-    BH_MASK_SET(concrete_struct->flags, !error_if_failed, Ast_Flag_Header_Check_No_Error);
-
-
-    i64 arg_count = bh_arr_length(ps_type->poly_params);
-    bh_arr_new(global_heap_allocator, concrete_struct->polymorphic_argument_types, arg_count);
-    bh_arr_set_length(concrete_struct->polymorphic_argument_types, arg_count);
-    concrete_struct->polymorphic_arguments = bh_arr_copy(global_heap_allocator, slns);
-
-    fori (i, 0, (i64) bh_arr_length(ps_type->poly_params)) {
-        concrete_struct->polymorphic_argument_types[i] = (AstType *) ast_clone(context.ast_alloc, ps_type->poly_params[i].type_node);
-    }
-
-    shput(ps_type->concrete_structs, unique_key, concrete_struct);
-    add_entities_for_node(NULL, (AstNode *) concrete_struct, sln_scope, NULL);
-    return NULL;
-}
diff --git a/src/symres.c b/src/symres.c
deleted file mode 100644 (file)
index 4410bb6..0000000
+++ /dev/null
@@ -1,1727 +0,0 @@
-#define BH_DEBUG
-#include "parser.h"
-#include "utils.h"
-#include "astnodes.h"
-#include "errors.h"
-
-// :EliminatingSymres - notes the places where too much work is being done in symbol resolution
-
-// Variables used during the symbol resolution phase.
-static Scope*       curr_scope    = NULL;
-static b32 report_unresolved_symbols = 1;
-static b32 resolved_a_symbol         = 0;
-
-// Everything related to waiting on is imcomplete at the moment.
-static Entity* waiting_on         = NULL;
-
-#define SYMRES(kind, ...) do { \
-    SymresStatus ss = symres_ ## kind (__VA_ARGS__); \
-    if (ss > Symres_Errors_Start) return ss;         \
-    } while (0)
-
-#define SYMRES_INVISIBLE(kind, node, ...) do { \
-    (node)->flags |= Ast_Flag_Symbol_Invisible; \
-    SymresStatus ss = symres_ ## kind (__VA_ARGS__); \
-    (node)->flags &= ~Ast_Flag_Symbol_Invisible; \
-    if (ss > Symres_Errors_Start) return ss;         \
-    } while (0)
-
-typedef enum SymresStatus {
-    Symres_Success,
-    Symres_Complete,
-    Symres_Goto_Parse,
-
-    Symres_Errors_Start,
-    Symres_Yield_Macro,
-    Symres_Yield_Micro,
-    Symres_Error,
-} SymresStatus;
-
-static SymresStatus symres_type(AstType** type);
-static SymresStatus symres_local(AstLocal** local);
-static SymresStatus symres_call(AstCall** pcall);
-static SymresStatus symres_size_of(AstSizeOf* so);
-static SymresStatus symres_align_of(AstAlignOf* so);
-static SymresStatus symres_field_access(AstFieldAccess** fa);
-static SymresStatus symres_compound(AstCompound* compound);
-static SymresStatus symres_expression(AstTyped** expr);
-static SymresStatus symres_return(AstReturn* ret);
-static SymresStatus symres_if(AstIfWhile* ifnode);
-static SymresStatus symres_while(AstIfWhile* whilenode);
-static SymresStatus symres_for(AstFor* fornode);
-static SymresStatus symres_case(AstSwitchCase *casenode);
-static SymresStatus symres_switch(AstSwitch* switchnode);
-static SymresStatus symres_use(AstUse* use);
-static SymresStatus symres_directive_solidify(AstDirectiveSolidify** psolid);
-static SymresStatus symres_directive_defined(AstDirectiveDefined** pdefined);
-static SymresStatus symres_directive_insert(AstDirectiveInsert* insert);
-static SymresStatus symres_statement_chain(AstNode** walker);
-static SymresStatus symres_statement(AstNode** stmt, b32 *remove);
-static SymresStatus symres_block(AstBlock* block);
-static SymresStatus symres_function_header(AstFunction* func);
-static SymresStatus symres_function(AstFunction* func);
-static SymresStatus symres_global(AstGlobal* global);
-static SymresStatus symres_overloaded_function(AstOverloadedFunction* ofunc);
-static SymresStatus symres_package(AstPackage* package);
-static SymresStatus symres_enum(AstEnumType* enum_node);
-static SymresStatus symres_memres_type(AstMemRes** memres);
-static SymresStatus symres_memres(AstMemRes** memres);
-static SymresStatus symres_struct_defaults(AstType* st);
-static SymresStatus symres_static_if(AstIf* static_if);
-static SymresStatus symres_macro(AstMacro* macro);
-static SymresStatus symres_constraint(AstConstraint* constraint);
-static SymresStatus symres_polyquery(AstPolyQuery *query);
-
-static void scope_enter(Scope* new_scope) {
-    curr_scope = new_scope;
-}
-
-static void scope_leave() {
-    curr_scope = curr_scope->parent;
-}
-
-static SymresStatus symres_symbol(AstNode** symbol_node) {
-    OnyxToken* token = (*symbol_node)->token;
-    AstNode* res = symbol_resolve(curr_scope, token);
-
-    if (!res) { // :SymresStall
-        if (report_unresolved_symbols) {
-            token_toggle_end(token);
-            char *closest = find_closest_symbol_in_scope_and_parents(curr_scope, token->text);
-            token_toggle_end(token);
-
-            if (closest) onyx_report_error(token->pos, Error_Critical, "Unable to resolve symbol '%b'. Did you mean '%s'?", token->text, token->length, closest);
-            else         onyx_report_error(token->pos, Error_Critical, "Unable to resolve symbol '%b'.", token->text, token->length);
-
-            return Symres_Error;
-        } else {
-            return Symres_Yield_Macro;
-        }
-
-    } else {
-        *symbol_node = res;
-        resolved_a_symbol = 1;
-    }
-
-    return Symres_Success;
-}
-
-static SymresStatus symres_struct_type(AstStructType* s_node) {
-    if (s_node->flags & Ast_Flag_Type_Is_Resolved) return Symres_Success;
-
-    s_node->flags |= Ast_Flag_Type_Is_Resolved;
-    s_node->flags |= Ast_Flag_Comptime;
-
-    if (s_node->scope) {
-        // FIX: This is probably wrong for the long term.
-        s_node->scope->parent = curr_scope;
-
-        scope_enter(s_node->scope);
-    }
-    
-    if (s_node->min_size_)      SYMRES(expression, &s_node->min_size_);
-    if (s_node->min_alignment_) SYMRES(expression, &s_node->min_alignment_);
-
-    if (s_node->polymorphic_argument_types) {
-        assert(s_node->polymorphic_arguments);
-
-        SymresStatus ss = Symres_Success, result;
-        fori (i, 0, (i64) bh_arr_length(s_node->polymorphic_argument_types)) {
-            result = symres_type(&s_node->polymorphic_argument_types[i]);
-            if (result > ss) ss = result;
-
-            if (s_node->polymorphic_arguments[i].value) {
-                result = symres_expression(&s_node->polymorphic_arguments[i].value);
-                if (result > ss) ss = result;
-            }
-        }
-    }
-
-    if (s_node->constraints.constraints) {
-        bh_arr_each(AstConstraint *, constraint, s_node->constraints.constraints) {
-            SYMRES(constraint, *constraint);
-        }
-    }
-
-    fori (i, 0, bh_arr_length(s_node->members)) {
-        AstStructMember *member = s_node->members[i];
-
-        if (member->type_node) {
-            SymresStatus ss = symres_type(&member->type_node);
-            if (ss != Symres_Success) {
-                s_node->flags &= ~Ast_Flag_Type_Is_Resolved;
-                if (s_node->scope) scope_leave();
-                return ss;
-            }
-        }
-    }
-
-    if (s_node->scope) scope_leave();
-    return Symres_Success;
-}
-
-static SymresStatus symres_type(AstType** type) {
-    // Don't make this kill all symbol resolution if the type is null.
-    if (!type || !*type) return Symres_Success;
-
-    switch ((*type)->kind) {
-        case Ast_Kind_Symbol:     SYMRES(symbol, (AstNode **) type); break;
-        case Ast_Kind_Basic_Type: break;
-        case Ast_Kind_Type_Alias: SYMRES(type, &((AstTypeAlias *) *type)->to); break;
-        case Ast_Kind_Field_Access: {
-            SYMRES(field_access, (AstFieldAccess **) type);
-            break;
-        }
-
-        case Ast_Kind_Pointer_Type: SYMRES(type, &((AstPointerType *) *type)->elem); break;
-        case Ast_Kind_Slice_Type:   SYMRES(type, &((AstSliceType *) *type)->elem); break;
-        case Ast_Kind_DynArr_Type:  SYMRES(type, &((AstDynArrType *) *type)->elem); break;
-        case Ast_Kind_VarArg_Type:  SYMRES(type, &((AstVarArgType *) *type)->elem); break;
-
-        case Ast_Kind_Function_Type: {
-            AstFunctionType* ftype = (AstFunctionType *) *type;
-
-            if (ftype->param_count > 0) {
-                fori (i, 0, (i64) ftype->param_count) {
-                    SYMRES(type, &ftype->params[i]);
-                }
-            }
-
-            SYMRES(type, &ftype->return_type);
-            break;
-        }
-
-        case Ast_Kind_Struct_Type: SYMRES(struct_type, (AstStructType *) *type); break;
-        case Ast_Kind_Array_Type: {
-            AstArrayType* a_node = (AstArrayType *) *type;
-
-            if (a_node->count_expr) SYMRES(expression, &a_node->count_expr);
-            SYMRES(type, &a_node->elem);
-            break;
-        }
-
-        case Ast_Kind_Enum_Type: break;
-
-        case Ast_Kind_Poly_Struct_Type: {
-            AstPolyStructType* pst_node = (AstPolyStructType *) *type;
-
-            if (pst_node->scope == NULL) {
-                pst_node->scope = scope_create(context.ast_alloc, pst_node->entity->scope, pst_node->token->pos);
-            }
-            break;
-        }
-
-        case Ast_Kind_Poly_Call_Type: {
-            AstPolyCallType* pc_node = (AstPolyCallType *) *type;
-
-            SYMRES(type, &pc_node->callee);
-
-            bh_arr_each(AstNode *, param, pc_node->params) {
-                if (node_is_type(*param)) {
-                    SYMRES(type, (AstType **) param);
-                } else {
-                    SYMRES(expression, (AstTyped **) param);
-                }
-            }
-            break;
-        }
-
-        case Ast_Kind_Type_Compound: {
-            AstCompoundType* ctype = (AstCompoundType *) *type;
-
-            bh_arr_each(AstType *, type, ctype->types) SYMRES(type, type);
-            break;
-        }
-
-        case Ast_Kind_Alias: {
-            AstAlias* alias = (AstAlias *) *type;
-            SYMRES_INVISIBLE(type, alias, (AstType **) &alias->alias);
-
-            break;
-        }
-
-        case Ast_Kind_Typeof: {
-            AstTypeOf* type_of = (AstTypeOf *) *type;
-            SYMRES(expression, &type_of->expr);
-            break;
-        }
-
-        case Ast_Kind_Distinct_Type: {
-            AstDistinctType *distinct = (AstDistinctType *) *type;
-            SYMRES(type, &distinct->base_type);
-            break;
-        }
-    }
-
-    return Symres_Success;
-}
-
-static SymresStatus symres_local(AstLocal** local) {
-    SYMRES(type, &(*local)->type_node);
-
-    if ((*local)->token != NULL)
-        symbol_introduce(curr_scope, (*local)->token, (AstNode *) *local);
-
-    return Symres_Success;
-}
-
-static SymresStatus symres_arguments(Arguments* args) {
-    bh_arr_each(AstTyped *, arg, args->values)
-        SYMRES(expression, arg);
-
-    bh_arr_each(AstNamedValue *, named_arg, args->named_values)
-        SYMRES(expression, &(*named_arg)->value);
-
-    return Symres_Success;
-}
-
-static SymresStatus symres_call(AstCall** pcall) {
-    AstCall *call = *pcall;
-    SYMRES(expression, (AstTyped **) &call->callee);
-    SYMRES(arguments, &call->args);
-
-    AstNode* callee = strip_aliases((AstNode *) call->callee);
-    if (callee->kind == Ast_Kind_Poly_Struct_Type) {
-        *pcall = (AstCall *) convert_call_to_polycall(call);
-        SYMRES(type, (AstType **) pcall);
-        return Symres_Success;
-    }
-
-    return Symres_Success;
-}
-
-static SymresStatus symres_size_of(AstSizeOf* so) {
-    SYMRES(type, &so->type_node);
-    SYMRES(type, &so->so_ast_type);
-
-    return Symres_Success;
-}
-
-static SymresStatus symres_align_of(AstAlignOf* ao) {
-    SYMRES(type, &ao->type_node);
-    SYMRES(type, &ao->ao_ast_type);
-
-    return Symres_Success;
-}
-
-static SymresStatus symres_field_access(AstFieldAccess** fa) {
-    if ((*fa)->expr == NULL) return Symres_Error;
-    SYMRES(expression, &(*fa)->expr);
-    if ((*fa)->expr == NULL) return Symres_Error;
-
-    AstTyped* expr = (AstTyped *) strip_aliases((AstNode *) (*fa)->expr);
-
-    b32 force_a_lookup = 0;
-
-    if (expr->kind == Ast_Kind_Enum_Type || expr->kind == Ast_Kind_Type_Raw_Alias) {
-        force_a_lookup = 1;
-    }
-
-    //
-    // If we are trying to access a field on an alias, we have to make sure
-    // the alias is "ready" to have a symbol looked up inside of it. This means
-    // the alias should have passed symbol resolution. If not, force a lookup
-    // and yield if the alias was not ready.
-    if ((*fa)->expr->kind == Ast_Kind_Alias) {
-        assert((*fa)->expr->entity);
-        if ((*fa)->expr->entity->state < Entity_State_Check_Types) {
-            force_a_lookup = 1;
-        }
-    }
-
-    AstNode* resolution = try_symbol_resolve_from_node((AstNode *) expr, (*fa)->token);
-    if (resolution) *((AstNode **) fa) = resolution;
-    else if (expr->kind == Ast_Kind_Package) {
-        if (report_unresolved_symbols) {
-            token_toggle_end((*fa)->token);
-            char *closest = find_closest_symbol_in_node((AstNode *) expr, (*fa)->token->text);
-            token_toggle_end((*fa)->token);
-
-            AstPackage *package = (AstPackage *) strip_aliases((AstNode *) (*fa)->expr);
-
-            if (closest) {
-                onyx_report_error((*fa)->token->pos, Error_Critical, "'%b' was not found in package '%s'. Did you mean '%s'?",
-                    (*fa)->token->text,
-                    (*fa)->token->length,
-                    package->package->name,
-                    closest);
-            } else {
-                onyx_report_error((*fa)->token->pos, Error_Critical, "'%b' was not found in package '%s'. Perhaps it is defined in a file that wasn't loaded?",
-                    (*fa)->token->text,
-                    (*fa)->token->length,
-                    package->package->name);
-            }
-            return Symres_Error;
-
-        } else {
-            return Symres_Yield_Macro;
-        }
-    }
-    else if (force_a_lookup) {
-        if (context.cycle_detected) {
-            onyx_report_error((*fa)->token->pos, Error_Critical, "'%b' does not exist here. This is a bad error message.",
-                (*fa)->token->text,
-                (*fa)->token->length);
-            return Symres_Error;
-        }
-
-        return Symres_Yield_Macro;
-    }
-
-    return Symres_Success;
-}
-
-static SymresStatus symres_compound(AstCompound* compound) {
-    bh_arr_each(AstTyped *, expr, compound->exprs) {
-        SYMRES(expression, expr);
-    }
-
-    return Symres_Success;
-}
-
-static SymresStatus symres_if_expression(AstIfExpression* if_expr) {
-    SYMRES(expression, &if_expr->cond);
-    SYMRES(expression, &if_expr->true_expr);
-    SYMRES(expression, &if_expr->false_expr);
-    return Symres_Success;
-}
-
-static SymresStatus symres_pipe(AstBinaryOp** pipe) {
-    AstCall* call_node = (AstCall *) (*pipe)->right;
-    SYMRES(expression, (AstTyped **) &call_node);
-    SYMRES(expression, &(*pipe)->left);
-
-    if (call_node->kind != Ast_Kind_Call) {
-        onyx_report_error((*pipe)->token->pos, Error_Critical, "Pipe operator expected call on right side.");
-        return Symres_Error;
-    }
-
-    if ((*pipe)->left == NULL) return Symres_Error;
-
-    // :EliminatingSymres
-    bh_arr_insertn(call_node->args.values, 0, 1);
-    call_node->args.values[0] = (AstTyped *) make_argument(context.ast_alloc, (*pipe)->left);
-    call_node->next = (*pipe)->next;
-
-    // NOTE: Not a BinaryOp node
-    *pipe = (AstBinaryOp *) call_node;
-
-    return Symres_Success;
-}
-
-// CLEANUP: This is an experimental feature and might be removed in the future.
-// I noticed a common pattern when writing in Onyx is something that looks like this:
-//
-//     foo.member_function(^foo, ...)
-//
-// I decided it would be worth adding a bit of syntactic sugar for such as call. I
-// decided to use the '->' operator for this purpose. The snippet below is the exact
-// same as the snippet above (after the nodes have been processed by the function below)
-//
-//     foo->member_function(...)
-static SymresStatus symres_method_call(AstBinaryOp** mcall) {
-    SYMRES(expression, &(*mcall)->left);
-    if ((*mcall)->left == NULL) return Symres_Error;
-
-    // :EliminatingSymres
-    if (((*mcall)->flags & Ast_Flag_Has_Been_Symres) == 0) {
-        if ((*mcall)->right->kind != Ast_Kind_Call) {
-            onyx_report_error((*mcall)->token->pos, Error_Critical, "'->' expected procedure call on right side.");
-            return Symres_Error;
-        }
-
-        AstFieldAccess* implicit_field_access = make_field_access(context.ast_alloc, (*mcall)->left, NULL);
-        implicit_field_access->token = ((AstCall *) (*mcall)->right)->callee->token;
-        ((AstCall *) (*mcall)->right)->callee = (AstTyped *) implicit_field_access;
-        (*mcall)->flags |= Ast_Flag_Has_Been_Symres;
-    }
-
-    assert((*mcall)->right->kind == Ast_Kind_Call);
-    SYMRES(expression, (AstTyped **) &(*mcall)->right);
-
-    return Symres_Success;
-}
-
-static SymresStatus symres_unaryop(AstUnaryOp** unaryop) {
-    if ((*unaryop)->operation == Unary_Op_Cast) {
-        SYMRES(type, &(*unaryop)->type_node);
-    }
-
-    SYMRES(expression, &(*unaryop)->expr);
-
-    return Symres_Success;
-}
-
-static SymresStatus symres_struct_literal(AstStructLiteral* sl) {
-    if (sl->stnode != NULL) SYMRES(expression, &sl->stnode);
-
-    // :EliminatingSymres
-    sl->type_node = (AstType *) sl->stnode;
-    while (sl->type_node && sl->type_node->kind == Ast_Kind_Type_Alias)
-        sl->type_node = ((AstTypeAlias *) sl->type_node)->to;
-
-    SYMRES(arguments, &sl->args);
-
-    return Symres_Success;
-}
-
-static SymresStatus symres_array_literal(AstArrayLiteral* al) {
-    if (al->atnode != NULL) SYMRES(expression, &al->atnode);
-
-    // :EliminatingSymres
-    al->type_node = (AstType *) al->atnode;
-    while (al->type_node && al->type_node->kind == Ast_Kind_Type_Alias)
-        al->type_node = ((AstTypeAlias *) al->type_node)->to;
-
-    bh_arr_each(AstTyped *, expr, al->values)
-        SYMRES(expression, expr);
-
-    return Symres_Success;
-}
-
-static SymresStatus symres_address_of(AstAddressOf** paof) {
-    AstAddressOf *aof = (AstAddressOf *) *paof;
-    SYMRES(expression, &aof->expr);
-
-    AstTyped *expr = (AstTyped *) strip_aliases((AstNode *) aof->expr);
-    if (node_is_type((AstNode *) expr)) {
-        AstPointerType *pt = onyx_ast_node_new(context.ast_alloc, sizeof(AstPointerType), Ast_Kind_Pointer_Type);
-        pt->token     = aof->token;
-        pt->elem      = (AstType *) expr;
-        pt->__unused  = aof->next;
-        *paof         = (AstAddressOf *) pt;
-        SYMRES(type, (AstType **) &pt);
-        return Symres_Success;
-    }
-
-    return Symres_Success;
-}
-
-static SymresStatus symres_expression(AstTyped** expr) {
-    if (node_is_type((AstNode *) *expr)) {
-        SYMRES(type, (AstType **) expr);
-        return Symres_Success;
-    }
-
-    switch ((*expr)->kind) {
-        case Ast_Kind_Symbol: SYMRES(symbol, (AstNode **) expr); break;
-
-        case Ast_Kind_Binary_Op:
-            SYMRES(expression, &((AstBinaryOp *)(*expr))->left);
-            SYMRES(expression, &((AstBinaryOp *)(*expr))->right);
-            break;
-
-        case Ast_Kind_Unary_Op:     SYMRES(unaryop, (AstUnaryOp **) expr); break;
-        case Ast_Kind_Call:         SYMRES(call, (AstCall **) expr); break;
-        case Ast_Kind_Argument:     SYMRES(expression, &((AstArgument *) *expr)->value); break;
-        case Ast_Kind_Block:        SYMRES(block, (AstBlock *) *expr); break;
-        case Ast_Kind_Dereference:  SYMRES(expression, &((AstDereference *)(*expr))->expr); break;
-        case Ast_Kind_Field_Access: SYMRES(field_access, (AstFieldAccess **) expr); break;
-        case Ast_Kind_Pipe:         SYMRES(pipe, (AstBinaryOp **) expr); break;
-        case Ast_Kind_Method_Call:  SYMRES(method_call, (AstBinaryOp **) expr); break;
-        case Ast_Kind_Size_Of:      SYMRES(size_of, (AstSizeOf *)*expr); break;
-        case Ast_Kind_Align_Of:     SYMRES(align_of, (AstAlignOf *)*expr); break;
-        case Ast_Kind_Address_Of:   SYMRES(address_of, (AstAddressOf **) expr); break;
-        case Ast_Kind_Alias: {
-            AstAlias *alias = (AstAlias *) *expr;
-            SYMRES_INVISIBLE(expression, alias, &alias->alias);
-            break;
-        }
-
-        case Ast_Kind_Range_Literal:
-            SYMRES(expression, &((AstRangeLiteral *)(*expr))->low);
-            SYMRES(expression, &((AstRangeLiteral *)(*expr))->high);
-
-            // :EliminatingSymres
-            SYMRES(type, &builtin_range_type);
-            (*expr)->type_node = builtin_range_type;
-            break;
-
-        case Ast_Kind_Function:
-        case Ast_Kind_NumLit:
-            SYMRES(type, &(*expr)->type_node);
-            break;
-
-        case Ast_Kind_StrLit: {
-            AstStrLit* str = (AstStrLit *) *expr;
-            if (str->is_cstr) {
-                SYMRES(type, &builtin_cstring_type);
-                str->type_node = builtin_cstring_type;
-
-            } else {
-                SYMRES(type, &builtin_string_type);
-                str->type_node = builtin_string_type;
-            }
-            break;
-        }
-
-        case Ast_Kind_Slice:
-        case Ast_Kind_Subscript:
-            SYMRES(expression, &((AstSubscript *)(*expr))->addr);
-            SYMRES(expression, &((AstSubscript *)(*expr))->expr);
-            break;
-
-        case Ast_Kind_Struct_Literal:
-            SYMRES(struct_literal, (AstStructLiteral *)(*expr));
-            break;
-
-        case Ast_Kind_Array_Literal:
-            SYMRES(array_literal, (AstArrayLiteral *)(*expr));
-            break;
-
-        case Ast_Kind_Directive_Solidify:
-            SYMRES(directive_solidify, (AstDirectiveSolidify **) expr);
-            break;
-
-        case Ast_Kind_Directive_Defined:
-            SYMRES(directive_defined, (AstDirectiveDefined **) expr);
-            break;
-
-        case Ast_Kind_Compound:
-            SYMRES(compound, (AstCompound *) *expr);
-            break;
-
-        case Ast_Kind_Package:
-            SYMRES(package, (AstPackage *) *expr);
-            break;
-
-        case Ast_Kind_If_Expression:
-            SYMRES(if_expression, (AstIfExpression *) *expr);
-            break;
-
-        case Ast_Kind_Directive_Insert:
-            SYMRES(directive_insert, (AstDirectiveInsert *) *expr);
-            break;
-
-        case Ast_Kind_Do_Block:
-            SYMRES(block, ((AstDoBlock *) *expr)->block);
-            break;
-
-        case Ast_Kind_Param:
-            if ((*expr)->flags & Ast_Flag_Param_Symbol_Dirty) {
-                assert((*expr)->token->type == Token_Type_Symbol);
-                *expr = (AstTyped *) make_symbol(context.ast_alloc, (*expr)->token);
-                SYMRES(expression, expr);
-            }
-            break;
-
-        default: break;
-    }
-
-    return Symres_Success;
-}
-
-static SymresStatus symres_return(AstReturn* ret) {
-    if (ret->expr)
-        SYMRES(expression, &ret->expr);
-
-    return Symres_Success;
-}
-
-static SymresStatus symres_if(AstIfWhile* ifnode) {
-    if (ifnode->kind == Ast_Kind_Static_If) {
-        if ((ifnode->flags & Ast_Flag_Static_If_Resolved) == 0) {
-            if (context.cycle_detected) {
-                onyx_report_error(ifnode->token->pos, Error_Waiting_On, "Waiting on static if resolution.");
-                return Symres_Error;
-            } else {
-                return Symres_Yield_Macro;
-            }
-        }
-
-        if (static_if_resolution(ifnode)) {
-            if (ifnode->true_stmt != NULL)  SYMRES(statement, (AstNode **) &ifnode->true_stmt, NULL);
-
-        } else {
-            if (ifnode->false_stmt != NULL) SYMRES(statement, (AstNode **) &ifnode->false_stmt, NULL);
-        }
-
-    } else {
-        if (ifnode->initialization != NULL) {
-            ifnode->scope = scope_create(context.ast_alloc, curr_scope, ifnode->token->pos);
-            scope_enter(ifnode->scope);
-
-            SYMRES(statement_chain, &ifnode->initialization);
-        }
-
-        SYMRES(expression, &ifnode->cond);
-
-        // NOTE: These are statements because "elseif" means the `false_stmt` has an if node.
-        if (ifnode->true_stmt != NULL)  SYMRES(statement, (AstNode **) &ifnode->true_stmt, NULL);
-        if (ifnode->false_stmt != NULL) SYMRES(statement, (AstNode **) &ifnode->false_stmt, NULL);
-
-        if (ifnode->initialization != NULL) scope_leave();
-    }
-
-    return Symres_Success;
-}
-
-static SymresStatus symres_while(AstIfWhile* whilenode) {
-    if (whilenode->initialization != NULL) {
-        whilenode->scope = scope_create(context.ast_alloc, curr_scope, whilenode->token->pos);
-        scope_enter(whilenode->scope);
-
-        SYMRES(statement_chain, &whilenode->initialization);
-    }
-
-    SYMRES(expression, &whilenode->cond);
-
-    if (whilenode->true_stmt)  SYMRES(block, whilenode->true_stmt);
-    if (whilenode->false_stmt) SYMRES(block, whilenode->false_stmt);
-
-    if (whilenode->initialization != NULL) scope_leave();
-
-    return Symres_Success;
-}
-
-static SymresStatus symres_for(AstFor* fornode) {
-    fornode->scope = scope_create(context.ast_alloc, curr_scope, fornode->token->pos);
-    scope_enter(fornode->scope);
-    SYMRES(expression, &fornode->iter);
-    SYMRES(local, &fornode->var);
-    SYMRES(block, fornode->stmt);
-    scope_leave();
-
-    return Symres_Success;
-}
-
-static SymresStatus symres_case(AstSwitchCase *casenode) {
-    if (!casenode->is_default) {
-        bh_arr_each(AstTyped *, expr, casenode->values) {
-            SYMRES(expression, expr);
-        }
-    }
-
-    SYMRES(block, casenode->block);
-    return Symres_Success;
-}
-
-static SymresStatus symres_switch(AstSwitch* switchnode) {
-    if (switchnode->initialization != NULL) {
-        switchnode->scope = scope_create(context.ast_alloc, curr_scope, switchnode->token->pos);
-        scope_enter(switchnode->scope);
-
-        SYMRES(statement_chain, &switchnode->initialization);
-    }
-
-    SYMRES(expression, &switchnode->expr);
-
-    if (switchnode->cases == NULL) {
-        SYMRES(block, switchnode->case_block);
-    } else {
-        bh_arr_each(AstSwitchCase *, pcase, switchnode->cases) {
-            SYMRES(case, *pcase);
-        }
-
-        if (switchnode->default_case) SYMRES(block, switchnode->default_case);
-    }
-
-    if (switchnode->switch_kind == Switch_Kind_Use_Equals && switchnode->case_exprs) {
-        bh_arr_each(CaseToBlock, ctb, switchnode->case_exprs) {
-            SYMRES(expression, (AstTyped **) &ctb->comparison);
-        }
-    }
-
-    if (switchnode->initialization != NULL) scope_leave();
-
-    return Symres_Success;
-}
-
-// CLEANUP: A lot of duplication going on in this function. A proper
-// "namespace" concept would be useful to remove a lot of the fluff
-// code here. There already is try_resolve_symbol_from_node which
-// may be able to do what is needed here?
-static SymresStatus symres_use(AstUse* use) {
-    SYMRES(expression, &use->expr);
-
-    AstTyped *use_expr = (AstTyped *) strip_aliases((AstNode *) use->expr);
-
-    // :EliminatingSymres
-    if (use_expr->kind == Ast_Kind_Package) {
-        AstPackage* package = (AstPackage *) use_expr;
-        SYMRES(package, package);
-
-        if (package->package->scope == curr_scope) return Symres_Success;
-
-        if (use->only == NULL) {
-            OnyxFilePos pos = { 0 };
-            if (use->token != NULL)
-                pos = use->token->pos;
-
-            scope_include(curr_scope, package->package->scope, pos);
-
-        } else {
-            bh_arr_each(QualifiedUse, qu, use->only) {
-                AstNode* thing = symbol_resolve(package->package->scope, qu->symbol_name);
-                if (thing == NULL) { // :SymresStall
-                    if (report_unresolved_symbols) {
-                        onyx_report_error(qu->symbol_name->pos, Error_Critical, 
-                                "The symbol '%b' was not found in this package.",
-                                qu->symbol_name->text, qu->symbol_name->length);
-                        return Symres_Error;
-                    } else {
-                        return Symres_Yield_Macro;
-                    }
-                }
-
-                symbol_introduce(curr_scope, qu->as_name, thing);
-            }
-        }
-
-        if (!use->entity) {
-            add_entities_for_node(NULL, (AstNode *) use, curr_scope, NULL);
-        }
-
-        package_track_use_package(package->package, use->entity);
-        return Symres_Success;
-    }
-
-    if (use_expr->kind == Ast_Kind_Foreign_Block) {
-        AstForeignBlock* fb = (AstForeignBlock *) use_expr;
-        if (fb->entity->state <= Entity_State_Resolve_Symbols) return Symres_Yield_Macro;
-
-        if (fb->scope == curr_scope) return Symres_Success;
-
-        if (use->only == NULL) {
-            OnyxFilePos pos = { 0 };
-            if (use->token != NULL)
-                pos = use->token->pos;
-
-            scope_include(curr_scope, fb->scope, pos);
-
-        } else {
-            bh_arr_each(QualifiedUse, qu, use->only) {
-                AstNode* thing = symbol_resolve(fb->scope, qu->symbol_name);
-                if (thing == NULL) { // :SymresStall
-                    if (report_unresolved_symbols) {
-                        onyx_report_error(qu->symbol_name->pos, Error_Critical, 
-                                "The symbol '%b' was not found in this package.",
-                                qu->symbol_name->text, qu->symbol_name->length);
-                        return Symres_Error;
-                    } else {
-                        return Symres_Yield_Macro;
-                    }
-                }
-
-                symbol_introduce(curr_scope, qu->as_name, thing);
-            }
-        }
-
-        return Symres_Success;
-    }
-
-    if (use_expr->kind == Ast_Kind_Enum_Type) {
-        AstEnumType* et = (AstEnumType *) use_expr;
-
-        bh_arr_each(AstEnumValue *, ev, et->values)
-            symbol_introduce(curr_scope, (*ev)->token, (AstNode *) *ev);
-
-        return Symres_Success;
-    }
-
-    if (use_expr->kind == Ast_Kind_Struct_Type) {
-        AstStructType* st = (AstStructType *) use_expr;
-        if (!st->scope) return Symres_Success;
-
-        if (use->only == NULL) {
-            scope_include(curr_scope, st->scope, use->token->pos);
-
-        } else {
-            bh_arr_each(QualifiedUse, qu, use->only) {
-                AstNode* thing = symbol_resolve(st->scope, qu->symbol_name);
-                if (thing == NULL) {
-                    onyx_report_error(qu->symbol_name->pos, Error_Critical, 
-                            "The symbol '%b' was not found in this scope.",
-                            qu->symbol_name->text, qu->symbol_name->length);
-                    return Symres_Error;
-                }
-
-                symbol_introduce(curr_scope, qu->as_name, thing);
-            }
-        }
-
-        return Symres_Success;
-    }
-
-    if (use_expr->type_node == NULL && use_expr->type == NULL) goto cannot_use;
-
-    // :EliminatingSymres
-    AstType* effective_type = use_expr->type_node;
-    if (effective_type->kind == Ast_Kind_Pointer_Type)
-        effective_type = ((AstPointerType *) effective_type)->elem;
-
-    if (effective_type->kind == Ast_Kind_Struct_Type ||
-            effective_type->kind == Ast_Kind_Poly_Call_Type) {
-
-        if (use_expr->type == NULL)
-            use_expr->type = type_build_from_ast(context.ast_alloc, use_expr->type_node);
-        if (use_expr->type == NULL) goto cannot_use;
-
-        Type* st = use_expr->type;
-        if (st->kind == Type_Kind_Pointer)
-            st = st->Pointer.elem;
-
-        fori (i, 0, shlen(st->Struct.members)) {
-            StructMember* value = st->Struct.members[i].value;
-            AstFieldAccess* fa = make_field_access(context.ast_alloc, use_expr, value->name);
-            symbol_raw_introduce(curr_scope, value->name, use->token->pos, (AstNode *) fa);
-        }
-
-        return Symres_Success;
-    }
-
-cannot_use:
-    onyx_report_error(use->token->pos, Error_Critical, "Cannot use this because its type is unknown.");
-    return Symres_Error;
-}
-
-static SymresStatus symres_directive_solidify(AstDirectiveSolidify** psolid) {
-    AstDirectiveSolidify* solid = *psolid;
-
-    SYMRES(expression, (AstTyped **) &solid->poly_proc);
-    if (solid->poly_proc && solid->poly_proc->kind == Ast_Kind_Directive_Solidify) {
-        AstFunction* potentially_resolved_proc = (AstFunction *) ((AstDirectiveSolidify *) solid->poly_proc)->resolved_proc;
-        if (!potentially_resolved_proc) return Symres_Yield_Micro;
-
-        solid->poly_proc = potentially_resolved_proc;
-    }
-
-    if (!solid->poly_proc || solid->poly_proc->kind != Ast_Kind_Polymorphic_Proc) {
-        onyx_report_error(solid->token->pos, Error_Critical, "Expected polymorphic procedure in #solidify directive.");
-        return Symres_Error;
-    }
-    
-    bh_arr_each(AstPolySolution, sln, solid->known_polyvars) {
-        // HACK: This assumes that 'ast_type' and 'value' are at the same offset.
-        SYMRES(expression, &sln->value);
-    }
-
-    return Symres_Success;
-}
-
-static SymresStatus symres_directive_defined(AstDirectiveDefined** pdefined) {
-    AstDirectiveDefined* defined = *pdefined;
-
-    b32 has_to_be_resolved = context.cycle_almost_detected;
-
-    resolved_a_symbol = 0;
-    SymresStatus ss = symres_expression(&defined->expr);
-    if (has_to_be_resolved && ss != Symres_Success && !resolved_a_symbol) {
-        // The symbol definitely was not found and there is no chance that it could be found.
-        defined->is_defined = 0;
-        return Symres_Success;
-    }
-
-    if (ss == Symres_Success) {
-        defined->is_defined = 1;
-        return Symres_Success;
-    }
-
-    return Symres_Yield_Macro;
-}
-
-static SymresStatus symres_directive_insert(AstDirectiveInsert* insert) {
-    SYMRES(expression, &insert->code_expr);
-    return Symres_Success;
-}
-
-static SymresStatus symres_statement(AstNode** stmt, b32 *remove) {
-    if (remove) *remove = 0;
-
-    switch ((*stmt)->kind) {
-        case Ast_Kind_Return:      SYMRES(return, (AstReturn *) *stmt);                  break;
-        case Ast_Kind_If:          SYMRES(if, (AstIfWhile *) *stmt);                     break;
-        case Ast_Kind_Static_If:   SYMRES(if, (AstIfWhile *) *stmt);                     break;
-        case Ast_Kind_While:       SYMRES(while, (AstIfWhile *) *stmt);                  break;
-        case Ast_Kind_For:         SYMRES(for, (AstFor *) *stmt);                        break;
-        case Ast_Kind_Switch:      SYMRES(switch, (AstSwitch *) *stmt);                  break;
-        case Ast_Kind_Call:        SYMRES(call, (AstCall **) stmt);                      break;
-        case Ast_Kind_Argument:    SYMRES(expression, (AstTyped **) &((AstArgument *) *stmt)->value); break;
-        case Ast_Kind_Block:       SYMRES(block, (AstBlock *) *stmt);                    break;
-        case Ast_Kind_Defer:       SYMRES(statement, &((AstDefer *) *stmt)->stmt, NULL); break;
-        case Ast_Kind_Switch_Case: SYMRES(case, (AstSwitchCase *) *stmt);                break;
-        case Ast_Kind_Jump:        break;
-        case Ast_Kind_Directive_Remove: break;
-
-        case Ast_Kind_Local:
-            // if (remove) *remove = 1;
-            SYMRES(local, (AstLocal **) stmt);
-            break;
-
-        case Ast_Kind_Use:
-            if (remove) *remove = 1;
-            SYMRES(use, (AstUse *) *stmt);
-            break;
-
-        default: SYMRES(expression, (AstTyped **) stmt); break;
-    }
-
-    return Symres_Success;
-}
-
-static SymresStatus symres_statement_chain(AstNode** walker) {
-    b32 remove = 0;
-
-    while (*walker) {
-        SYMRES(statement, walker, &remove);
-        if (remove) {
-            remove = 0;
-            AstNode* tmp = (*walker)->next;
-            (*walker)->next = NULL;
-            (*walker) = tmp;
-
-        } else {
-            walker = &(*walker)->next;
-        }
-    }
-    return Symres_Success;
-}
-
-static SymresStatus symres_block(AstBlock* block) {
-    if (block->rules & Block_Rule_New_Scope) {
-        if (block->scope == NULL)
-            block->scope = scope_create(context.ast_alloc, curr_scope, block->token->pos);
-
-        scope_enter(block->scope);
-    }
-
-    if (block->binding_scope != NULL)
-        scope_include(curr_scope, block->binding_scope, block->token->pos);
-
-    if (block->body) {
-        AstNode** start = &block->body;
-        fori (i, 0, block->statement_idx) {
-            start = &(*start)->next;
-        }
-
-        b32 remove = 0;
-
-        while (*start) {
-            SymresStatus cs = symres_statement(start, &remove);
-
-            if (remove) {
-                remove = 0;
-                AstNode* tmp = (*start)->next;
-                (*start)->next = NULL;
-                (*start) = tmp;
-
-            } else {
-                switch (cs) {
-                    case Symres_Success:
-                        start = &(*start)->next;
-                        block->statement_idx++;
-                        break;
-
-                    default:
-                        return cs;
-                }
-            }
-        }
-
-        block->statement_idx = 0;
-    }
-
-    if (block->rules & Block_Rule_New_Scope)
-        scope_leave();
-
-    return Symres_Success;
-}
-
-SymresStatus symres_function_header(AstFunction* func) {
-    func->flags |= Ast_Flag_Comptime;
-
-    if (func->scope == NULL)
-        func->scope = scope_create(context.ast_alloc, curr_scope, func->token->pos);
-
-    if (func->constraints.constraints != NULL && func->constraints.constraints_met == 0) {
-        bh_arr_each(AstConstraint *, constraint, func->constraints.constraints) {
-            SYMRES(constraint, *constraint);
-        }
-
-        // Return early here to finish checking constraints in the checker.
-        // Will resume here after constraints have been met.
-        return Symres_Success;
-    }
-
-    scope_enter(func->scope);
-
-    bh_arr_each(AstParam, param, func->params) {
-        if (param->default_value != NULL) {
-            SYMRES(expression, &param->default_value);
-            if (onyx_has_errors()) return Symres_Error;
-        }
-    }
-
-    bh_arr_each(AstParam, param, func->params) {
-        symbol_introduce(curr_scope, param->local->token, (AstNode *) param->local);
-    }
-
-    bh_arr_each(AstParam, param, func->params) {
-        if (param->local->type_node != NULL) {
-            SYMRES_INVISIBLE(type, param->local, &param->local->type_node);
-        }
-    }
-
-    if (potentially_convert_function_to_polyproc(func)) {
-        return Symres_Complete;
-    }
-
-    if (func->nodes_that_need_entities_after_clone && bh_arr_length(func->nodes_that_need_entities_after_clone) > 0 && func->entity) {
-        bh_arr_each(AstNode *, node, func->nodes_that_need_entities_after_clone) {
-            // This makes a lot of assumptions about how these nodes are being processed,
-            // and I don't want to start using this with other nodes without considering
-            // what the ramifications of that is.
-            assert((*node)->kind == Ast_Kind_Static_If || (*node)->kind == Ast_Kind_File_Contents);
-
-            // Need to curr_scope->parent because curr_scope is the function body scope.
-            Scope *scope = curr_scope->parent;
-
-            if ((*node)->kind == Ast_Kind_Static_If) {
-                AstIf *static_if = (AstIf *) *node;
-                assert(static_if->defined_in_scope);
-                scope = static_if->defined_in_scope;
-
-                if (func->poly_scope) {
-                    scope = scope_create(context.ast_alloc, scope, static_if->token->pos);
-                    scope_include(scope, func->poly_scope, static_if->token->pos);
-                }
-            }
-
-            add_entities_for_node(NULL, *node, scope, func->entity->package);
-        }
-
-        bh_arr_set_length(func->nodes_that_need_entities_after_clone, 0);
-    }
-
-    SYMRES(type, &func->return_type);
-
-    scope_leave();
-
-    return Symres_Success;
-}
-
-SymresStatus symres_function(AstFunction* func) {
-    if (func->entity_header && func->entity_header->state < Entity_State_Check_Types) return Symres_Yield_Macro;
-    if (func->kind == Ast_Kind_Polymorphic_Proc) return Symres_Complete;
-    assert(func->scope);
-
-    scope_enter(func->scope);
-
-    if ((func->flags & Ast_Flag_Has_Been_Symres) == 0) {
-        // :EliminatingSymres
-        bh_arr_each(AstParam, param, func->params) {
-            // CLEANUP: Currently, in order to 'use' parameters, the type must be completely
-            // resolved and built. This is excessive because all that should need to be known
-            // is the names of the members, since all that happens is implicit field accesses
-            // are placed in the scope. So instead, there should be a way to just query all the
-            // member names in the structure, without needing to know their type. This would be
-            // easy if it were not for 'use' statements in structs. It is made even more complicated
-            // by this situtation:
-            //
-            //     Foo :: struct (T: type_expr) {
-            //         use t : T;
-            //
-            //         something_else := 5 + 6 * 8;
-            //     }
-            //
-            // The 'use t : T' member requires completely knowing the type of T, to know which
-            // members should be brought in. At the moment, that requires completely building the
-            // type of Foo($T).
-            if (param->is_used && !param->use_processed) {
-                if (param->local->type_node != NULL && param->local->type == NULL) {
-                    param->local->type = type_build_from_ast(context.ast_alloc, param->local->type_node);
-
-                    if (param->local->type == NULL) return Symres_Yield_Macro;
-                }
-
-                if (type_is_struct(param->local->type)) {
-                    Type* st;
-                    if (param->local->type->kind == Type_Kind_Struct) {
-                        st = param->local->type;
-                    } else {
-                        st = param->local->type->Pointer.elem;
-                    }
-
-                    if (st->Struct.status != SPS_Uses_Done) return Symres_Yield_Macro;
-
-                    fori (i, 0, shlen(st->Struct.members)) {
-                        StructMember* value = st->Struct.members[i].value;
-                        AstFieldAccess* fa = make_field_access(context.ast_alloc, (AstTyped *) param->local, value->name);
-                        symbol_raw_introduce(curr_scope, value->name, param->local->token->pos, (AstNode *) fa);
-                    }
-
-                    param->use_processed = 1;
-
-                } else if (param->local->type != NULL) {
-                    onyx_report_error(param->local->token->pos, Error_Critical, "Can only 'use' structures or pointers to structures.");
-
-                } else {
-                    // :ExplicitTyping
-                    onyx_report_error(param->local->token->pos, Error_Critical, "Cannot deduce type of parameter '%b'; Try adding it explicitly.",
-                        param->local->token->text,
-                        param->local->token->length);
-                }
-            }
-        }
-
-        bh_arr_each(AstTyped *, pexpr, func->tags) {
-            SYMRES(expression, pexpr);
-        }
-
-        func->flags |= Ast_Flag_Has_Been_Symres;
-    }
-
-    SYMRES(block, func->body);
-
-    scope_leave();
-    return Symres_Success;
-}
-
-static SymresStatus symres_global(AstGlobal* global) {
-    SYMRES(type, &global->type_node);
-    return Symres_Success;
-}
-
-static SymresStatus symres_overloaded_function(AstOverloadedFunction* ofunc) {
-    bh_arr_each(OverloadOption, overload, ofunc->overloads) {
-        SYMRES(expression, &overload->option);
-    }
-    return Symres_Success;
-}
-
-static SymresStatus symres_package(AstPackage* package) {
-    if (package->package == NULL) {
-        if (!package->package_name) return Symres_Error;
-
-        package->package = package_lookup(package->package_name);
-    }
-
-    if (package->package) {
-        return Symres_Success;
-    } else {
-        if (report_unresolved_symbols) {
-            onyx_report_error(package->token->pos, Error_Critical,
-                    "Package '%s' not found in included source files.",
-                    package->package_name);
-            return Symres_Error;
-        } else {
-            return Symres_Yield_Macro;
-        }
-    }
-}
-
-static SymresStatus symres_enum(AstEnumType* enum_node) {
-    if (enum_node->backing->kind == Ast_Kind_Symbol) SYMRES(symbol, (AstNode **) &enum_node->backing);
-    if (enum_node->backing == NULL) return Symres_Error;
-
-    if (enum_node->scope == NULL) {
-        enum_node->backing_type = type_build_from_ast(context.ast_alloc, enum_node->backing);
-        enum_node->scope = scope_create(context.ast_alloc, curr_scope, enum_node->token->pos);
-
-        type_build_from_ast(context.ast_alloc, (AstType *) enum_node);
-    }
-
-    scope_enter(enum_node->scope);
-
-    // :EliminatingSymres
-    u64 next_assign_value = enum_node->is_flags ? 1 : 0;
-    bh_arr_each(AstEnumValue *, value, enum_node->values) {
-        if ((*value)->flags & Ast_Flag_Has_Been_Checked) continue;
-
-        (*value)->type = enum_node->etcache;
-        (*value)->flags |= Ast_Flag_Comptime;
-
-        if ((*value)->value != NULL) {
-            SYMRES(expression, &(*value)->value);
-
-            if ((*value)->value->kind == Ast_Kind_Enum_Value) {
-                (*value)->value = ((AstEnumValue *) (*value)->value)->value;
-                (*value)->value->type = enum_node->etcache;
-            }
-
-            if ((*value)->value->kind == Ast_Kind_NumLit) {
-                AstNumLit *n_value = (AstNumLit *) (*value)->value;
-                resolve_expression_type((AstTyped *) n_value);
-
-                if (type_is_small_integer(n_value->type)) {
-                    next_assign_value = n_value->value.i;
-                } else if (type_is_integer(n_value->type)) {
-                    next_assign_value = n_value->value.l;
-                } else {
-                    onyx_report_error((*value)->token->pos, Error_Critical, "expected numeric integer literal for enum initialization, got '%s'", type_get_name(n_value->type));
-                    return Symres_Error;
-                }
-
-                n_value->type = enum_node->etcache;
-
-            } else {
-                if ((*value)->entity == NULL) {
-                    add_entities_for_node(NULL, (AstNode *) (*value), enum_node->scope, NULL);
-                }
-
-                if (context.cycle_detected) {
-                    onyx_report_error((*value)->token->pos, Error_Critical, "Expected compile time known value for enum initialization.");
-                    return Symres_Error;
-                }
-
-                return Symres_Yield_Macro;
-            }
-
-        } else {
-            AstNumLit* num = make_int_literal(context.ast_alloc, next_assign_value);
-            num->type = enum_node->etcache;
-
-            (*value)->value = (AstTyped *) num;
-        }
-
-        symbol_introduce(enum_node->scope, (*value)->token, (AstNode *) (*value));
-
-        (*value)->flags |= Ast_Flag_Comptime | Ast_Flag_Has_Been_Checked;
-
-        if (enum_node->is_flags) {
-            next_assign_value <<= 1;
-        } else {
-            next_assign_value++;
-        }
-    }
-
-    scope_leave();
-
-    // HACK this ensure that you can only lookup symbols in an Enum that are actually defined in the enum.
-    // However, during the symbol resolution of the values in an enum, they need to be able to see the
-    // enclosing scope.
-    enum_node->scope->parent = NULL;
-
-    return Symres_Success;
-}
-
-static SymresStatus symres_memres_type(AstMemRes** memres) {
-    SYMRES(type, &(*memres)->type_node);
-    return Symres_Success;
-}
-
-static SymresStatus symres_memres(AstMemRes** memres) {
-    if ((*memres)->initial_value != NULL) {
-        SYMRES(expression, &(*memres)->initial_value);
-    }
-    return Symres_Success;
-}
-
-static SymresStatus symres_struct_defaults(AstType* t) {
-    if (t->kind != Ast_Kind_Struct_Type) return Symres_Error;
-
-    AstStructType* st = (AstStructType *) t;
-    if (st->scope) scope_enter(st->scope);
-
-    if (st->meta_tags) {
-        bh_arr_each(AstTyped *, meta, st->meta_tags) {
-            SYMRES(expression, meta);
-        }
-    }
-
-    bh_arr_each(AstStructMember *, smem, st->members) {
-        if ((*smem)->initial_value != NULL) {
-            SYMRES(expression, &(*smem)->initial_value);
-        }
-
-        if ((*smem)->meta_tags != NULL) {
-            bh_arr_each(AstTyped *, meta, (*smem)->meta_tags) {
-                SYMRES(expression, meta);
-            }
-        }
-    }
-
-    if (st->scope) scope_leave();
-    return Symres_Success;
-}
-
-static SymresStatus symres_polyproc(AstFunction* pp) {
-    pp->flags |= Ast_Flag_Comptime;
-    pp->parent_scope_of_poly_proc = curr_scope;
-    return Symres_Success;
-}
-
-static SymresStatus symres_static_if(AstIf* static_if) {
-    if (static_if->flags & Ast_Flag_Dead) return Symres_Complete;
-
-    SYMRES(expression, &static_if->cond);
-    return Symres_Success;
-}
-
-static SymresStatus symres_process_directive(AstNode* directive) {
-    // :EliminatingSymres
-    switch (directive->kind) {
-        case Ast_Kind_Directive_Add_Overload: {
-            AstDirectiveAddOverload *add_overload = (AstDirectiveAddOverload *) directive;
-
-            SYMRES(expression, (AstTyped **) &add_overload->overloaded_function);
-            if (add_overload->overloaded_function == NULL) return Symres_Error; // NOTE: Error message will already be generated
-
-            if (add_overload->overloaded_function->kind != Ast_Kind_Overloaded_Function) {
-                onyx_report_error(add_overload->token->pos, Error_Critical, "#match directive expects a matched procedure.");
-                return Symres_Error;
-            }
-
-            AstOverloadedFunction* ofunc = (AstOverloadedFunction *) add_overload->overloaded_function;
-            if (ofunc->locked) {
-                onyx_report_error(add_overload->token->pos, Error_Critical, "Cannot add match option here as the original #match was declared as #locked.");
-                onyx_report_error(ofunc->token->pos, Error_Critical, "Here is the original #match.");
-                return Symres_Error;
-            }
-
-            if (ofunc->only_local_functions) {
-                if (!token_same_file(add_overload->token, ofunc->token)) {
-                    onyx_report_error(add_overload->token->pos, Error_Critical, "Cannot add match option here as this option is not within the same file as the original #match declared with #local.");
-                    onyx_report_error(ofunc->token->pos, Error_Critical, "Here is the original #match.");
-                }
-            }
-
-            SYMRES(expression, (AstTyped **) &add_overload->overload);
-            add_overload_option(&ofunc->overloads, add_overload->precedence, add_overload->overload);
-            break;
-        }
-
-        case Ast_Kind_Directive_Operator: {
-            AstDirectiveOperator *operator = (AstDirectiveOperator *) directive;
-            SYMRES(expression, &operator->overload);
-            if (!operator->overload) return Symres_Error;
-
-            AstFunction* overload = get_function_from_node((AstNode *) operator->overload);
-            if (overload == NULL) {
-                onyx_report_error(operator->token->pos, Error_Critical, "This cannot be used as an operator overload.");
-                return Symres_Error;
-            }
-
-            if (operator->operator != Binary_Op_Subscript_Equals && bh_arr_length(overload->params) != 2) {
-                onyx_report_error(operator->token->pos, Error_Critical, "Expected exactly 2 arguments for binary operator overload.");
-                return Symres_Error;
-            }
-
-            add_overload_option(&operator_overloads[operator->operator], 0, operator->overload);
-            break;
-        }
-
-        case Ast_Kind_Directive_Export: {
-            AstDirectiveExport *export = (AstDirectiveExport *) directive;
-            SYMRES(expression, &export->export);
-            SYMRES(expression, &export->export_name_expr);
-
-            if (export->export->kind == Ast_Kind_Polymorphic_Proc) {
-                onyx_report_error(export->token->pos, Error_Critical, "Cannot export a polymorphic function.");
-                return Symres_Error;
-            }
-
-            if (export->export->kind == Ast_Kind_Function) {
-                AstFunction *func = (AstFunction *) export->export;
-                func->exported_name = export->export_name;
-                func->is_exported = 1;
-
-                if (func->is_exported) {
-                    if (func->is_foreign) {
-                        onyx_report_error(export->token->pos, Error_Critical, "Cannot export a foreign function.");
-                        return Symres_Error;
-                    }
-
-                    if (func->is_intrinsic) {
-                        onyx_report_error(export->token->pos, Error_Critical, "Cannot export an intrinsic function.");
-                        return Symres_Error;
-                    }
-                }
-            }
-
-            break;
-        }
-
-        case Ast_Kind_Directive_Init: {
-            AstDirectiveInit *init = (AstDirectiveInit *) directive;
-            SYMRES(expression, &init->init_proc);
-
-            if (init->dependencies) {
-                bh_arr_each(AstDirectiveInit *, dependency, init->dependencies) {
-                    SYMRES(expression, (AstTyped **) dependency);
-                }
-            }
-
-            break;
-        }
-
-        case Ast_Kind_Directive_Library: {
-            AstDirectiveLibrary *library = (AstDirectiveLibrary *) directive;
-            SYMRES(expression, &library->library_symbol);
-            break;
-        }
-
-        case Ast_Kind_Injection: {
-            AstInjection *inject = (AstInjection *) directive;
-
-            if (inject->dest == NULL) {
-                if (inject->full_loc == NULL) return Symres_Error;
-
-                if (inject->full_loc->kind != Ast_Kind_Field_Access) {
-                    onyx_report_error(inject->token->pos, Error_Critical, "#inject expects a dot (a.b) expression for the injection point.");
-                    return Symres_Error;
-                }
-
-                AstFieldAccess *acc = (AstFieldAccess *) inject->full_loc;
-                inject->dest = acc->expr;
-                inject->symbol = acc->token;
-            }
-
-            SYMRES(expression, &inject->dest);
-            SYMRES(expression, &inject->to_inject);
-
-            Scope *scope = get_scope_from_node_or_create((AstNode *) inject->dest);
-            if (scope == NULL) {
-                onyx_report_error(inject->token->pos, Error_Critical, "Cannot #inject here.");
-                return Symres_Error;
-            }
-
-            AstBinding *binding = onyx_ast_node_new(context.ast_alloc, sizeof(AstBinding), Ast_Kind_Binding);
-            binding->token = inject->symbol;
-            binding->node = (AstNode *) inject->to_inject;
-
-            Package *pac = NULL;
-            if (inject->dest->kind == Ast_Kind_Package) {
-                pac = ((AstPackage *) inject->dest)->package;
-            }
-
-            add_entities_for_node(NULL, (AstNode *) binding, scope, pac);
-            return Symres_Complete;
-        }
-    }
-
-    return Symres_Success;
-}
-
-static SymresStatus symres_macro(AstMacro* macro) {
-    macro->flags |= Ast_Flag_Comptime;
-
-    if (macro->body->kind == Ast_Kind_Function) {
-        SYMRES(function_header, (AstFunction *) macro->body);
-    }
-    else if (macro->body->kind == Ast_Kind_Polymorphic_Proc) {
-        SYMRES(polyproc, (AstFunction *) macro->body);
-    }
-
-    return Symres_Success;
-}
-
-static SymresStatus symres_constraint(AstConstraint* constraint) {
-    switch (constraint->phase) {
-        case Constraint_Phase_Cloning_Expressions:
-        case Constraint_Phase_Waiting_To_Be_Queued: {
-            SYMRES(expression, (AstTyped **) &constraint->interface);
-
-            bh_arr_each(AstType *, type_arg, constraint->type_args) {
-                SYMRES(type, type_arg);
-            }
-
-            return Symres_Success;
-        }
-
-        case Constraint_Phase_Checking_Expressions: {
-            fori (i, constraint->expr_idx, bh_arr_length(constraint->exprs)) {
-                SYMRES(expression, &constraint->exprs[i].expr);
-
-                if (constraint->exprs[i].expected_type_expr) {
-                    SYMRES(type, &constraint->exprs[i].expected_type_expr);
-                }
-            }
-
-            return Symres_Success;
-        }
-    }
-
-    return Symres_Success;
-}
-
-static SymresStatus symres_polyquery(AstPolyQuery *query) {
-    // :EliminatingSymres
-    query->successful_symres = 0;
-
-    if (query->function_header->scope == NULL)
-        query->function_header->scope = scope_create(context.ast_alloc, query->proc->parent_scope_of_poly_proc, query->token->pos);
-
-    scope_enter(query->function_header->scope);
-
-    u32 idx = 0;
-    bh_arr_each(AstParam, param, query->function_header->params) {
-        bh_arr_each(AstPolyParam, pp, query->proc->poly_params) {
-            if (pp->kind == PPK_Baked_Value && pp->idx == idx) goto skip_introducing_symbol;
-        }
-
-        symbol_introduce(curr_scope, param->local->token, (AstNode *) param->local);
-
-    skip_introducing_symbol:
-        idx++;
-    }
-
-    bh_arr_each(AstParam, param, query->function_header->params) {
-        if (param->local->type_node != NULL) {
-            resolved_a_symbol = 0;
-
-            param->local->flags |= Ast_Flag_Symbol_Invisible;
-            symres_type(&param->local->type_node);
-            param->local->flags &= ~Ast_Flag_Symbol_Invisible;
-
-            onyx_clear_errors();
-
-            if (resolved_a_symbol) query->successful_symres = 1;
-        }
-    }
-
-    scope_leave();
-    return Symres_Success;
-}
-
-static SymresStatus symres_foreign_block(AstForeignBlock *fb) {
-    if (fb->scope == NULL)
-        fb->scope = scope_create(context.ast_alloc, curr_scope, fb->token->pos);
-
-    bh_arr_each(Entity *, pent, fb->captured_entities) {
-        Entity *ent = *pent;
-        if (ent->type == Entity_Type_Function_Header) {
-            if (ent->function->body->next != NULL) {
-                onyx_report_error(ent->function->token->pos, Error_Critical, "Procedures declared in a #foreign block should not have bodies.");
-                return Symres_Error;
-            }
-
-            ent->function->foreign_name = ent->function->intrinsic_name; // Hmm... This might not be right?
-            ent->function->foreign_module = fb->module_name;
-            ent->function->is_foreign = 1;
-            ent->function->entity = NULL;
-            ent->function->entity_header = NULL;
-            ent->function->entity_body = NULL;
-
-            add_entities_for_node(NULL, (AstNode *) ent->function, ent->scope, ent->package);
-            continue;
-        }
-
-        if (ent->type == Entity_Type_Binding) {
-            AstBinding* new_binding = onyx_ast_node_new(context.ast_alloc, sizeof(AstBinding), Ast_Kind_Binding);
-            new_binding->token = ent->binding->token;
-            new_binding->node = ent->binding->node;
-
-            Entity e;
-            memset(&e, 0, sizeof(e));
-            e.type = Entity_Type_Binding;
-            e.state = Entity_State_Introduce_Symbols;
-            e.binding = new_binding;
-            e.scope = fb->scope;
-            e.package = ent->package;
-
-            entity_heap_insert(&context.entities, e);
-        }
-
-        if (ent->type != Entity_Type_Function) {
-            entity_heap_insert_existing(&context.entities, ent);
-        }
-    }
-
-    return Symres_Complete;
-}
-
-static SymresStatus symres_include(AstInclude* include) {
-    if (include->name != NULL) return Symres_Goto_Parse;
-
-    SYMRES(expression, &include->name_node);
-
-    if (include->name_node->kind != Ast_Kind_StrLit) {
-        onyx_report_error(include->token->pos, Error_Critical, "Expected compile-time known string literal here. Got '%s'.", onyx_ast_node_kind_string(include->name_node->kind));
-        return Symres_Error;
-    }
-
-    OnyxToken* str_token = include->name_node->token;
-    if (str_token != NULL) {
-        token_toggle_end(str_token);
-        include->name = bh_strdup(context.ast_alloc, str_token->text);
-        string_process_escape_seqs(include->name, include->name, strlen(include->name));
-        token_toggle_end(str_token);
-    }
-
-    return Symres_Goto_Parse;
-}
-
-static SymresStatus symres_file_contents(AstFileContents* fc) {
-    SYMRES(expression, &fc->filename_expr);
-
-    if (fc->filename_expr->kind != Ast_Kind_StrLit) {
-        onyx_report_error(fc->token->pos, Error_Critical, "Expected given expression to be a compile-time stirng literal.");
-        return Symres_Error;
-    }
-
-    return Symres_Success;
-}
-
-void symres_entity(Entity* ent) {
-    if (ent->scope) scope_enter(ent->scope);
-
-    report_unresolved_symbols = context.cycle_detected;
-
-    SymresStatus ss = Symres_Success;
-    EntityState next_state = Entity_State_Check_Types;
-
-    switch (ent->type) {
-        case Entity_Type_Binding: {
-            symbol_introduce(curr_scope, ent->binding->token, ent->binding->node);
-            package_reinsert_use_packages(ent->package);
-            next_state = Entity_State_Finalized;
-            break;
-        }
-
-        case Entity_Type_Static_If:               ss = symres_static_if(ent->static_if); break;
-
-        case Entity_Type_Load_Path:
-        case Entity_Type_Load_File:               ss = symres_include(ent->include); break;
-        case Entity_Type_File_Contents:           ss = symres_file_contents(ent->file_contents); break;
-
-        case Entity_Type_Foreign_Function_Header:
-        case Entity_Type_Temp_Function_Header:
-        case Entity_Type_Function_Header:         ss = symres_function_header(ent->function); break;
-        case Entity_Type_Function:                ss = symres_function(ent->function);        break;
-
-        case Entity_Type_Global_Header:           ss = symres_global(ent->global); break;
-
-        case Entity_Type_Use_Package:
-        case Entity_Type_Use:                     ss = symres_use(ent->use);
-                                                  next_state = Entity_State_Finalized;
-                                                  break;
-
-        case Entity_Type_Polymorphic_Proc:        ss = symres_polyproc(ent->poly_proc);
-                                                  next_state = Entity_State_Finalized;
-                                                  break;
-
-        case Entity_Type_Overloaded_Function:     ss = symres_overloaded_function(ent->overloaded_function); break;
-        case Entity_Type_Expression:              ss = symres_expression(&ent->expr); break;
-        case Entity_Type_Type_Alias:              ss = symres_type(&ent->type_alias); break;
-        case Entity_Type_Enum:                    ss = symres_enum(ent->enum_type); break;
-        case Entity_Type_Memory_Reservation_Type: ss = symres_memres_type(&ent->mem_res); break;
-        case Entity_Type_Memory_Reservation:      ss = symres_memres(&ent->mem_res); break;
-        case Entity_Type_String_Literal:          ss = symres_expression(&ent->expr); break;
-        case Entity_Type_Struct_Member_Default:   ss = symres_struct_defaults((AstType *) ent->type_alias); break;
-        case Entity_Type_Process_Directive:       ss = symres_process_directive((AstNode *) ent->expr); break;
-        case Entity_Type_Macro:                   ss = symres_macro(ent->macro); break;
-        case Entity_Type_Constraint_Check:        ss = symres_constraint(ent->constraint); break;
-        case Entity_Type_Polymorph_Query:         ss = symres_polyquery(ent->poly_query); break;
-        case Entity_Type_Foreign_Block:           ss = symres_foreign_block(ent->foreign_block);
-                                                  if (context.options->generate_foreign_info) {
-                                                      next_state = Entity_State_Check_Types;
-                                                      ss = Symres_Success;
-                                                  }
-                                                  break;
-
-        default: break;
-    }
-
-    if (ss == Symres_Yield_Macro) ent->macro_attempts++;
-    if (ss == Symres_Yield_Micro) ent->micro_attempts++;
-    if (ss == Symres_Complete)    ent->state = Entity_State_Finalized;
-    if (ss == Symres_Goto_Parse)  ent->state = Entity_State_Parse;
-    if (ss == Symres_Success) {
-        ent->macro_attempts = 0;
-        ent->micro_attempts = 0;
-        ent->state = next_state;
-    }
-
-    curr_scope = NULL;
-}
diff --git a/src/types.c b/src/types.c
deleted file mode 100644 (file)
index 0d9568a..0000000
+++ /dev/null
@@ -1,1490 +0,0 @@
-#define BH_DEBUG
-#include "stb_ds.h"
-#include "types.h"
-#include "astnodes.h"
-#include "utils.h"
-#include "errors.h"
-
-// NOTE: These have to be in the same order as Basic
-Type basic_types[] = {
-    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_void, { Basic_Kind_Void,                    0,                       0,  1, "void"   } },
-
-    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_bool, { Basic_Kind_Bool,   Basic_Flag_Boolean,                       1,  1, "bool"   } },
-
-    { Type_Kind_Basic, 0, 0, NULL,                        { Basic_Kind_Int_Unsized, Basic_Flag_Integer,                  0,  0, "unsized int" } },
-    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_i8,  { Basic_Kind_I8,     Basic_Flag_Integer,                       1,  1, "i8"     } },
-    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_u8,  { Basic_Kind_U8,     Basic_Flag_Integer | Basic_Flag_Unsigned, 1,  1, "u8"     } },
-    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_i16, { Basic_Kind_I16,    Basic_Flag_Integer,                       2,  2, "i16"    } },
-    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_u16, { Basic_Kind_U16,    Basic_Flag_Integer | Basic_Flag_Unsigned, 2,  2, "u16"    } },
-    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_i32, { Basic_Kind_I32,    Basic_Flag_Integer,                       4,  4, "i32"    } },
-    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_u32, { Basic_Kind_U32,    Basic_Flag_Integer | Basic_Flag_Unsigned, 4,  4, "u32"    } },
-    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_i64, { Basic_Kind_I64,    Basic_Flag_Integer,                       8,  8, "i64"    } },
-    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_u64, { Basic_Kind_U64,    Basic_Flag_Integer | Basic_Flag_Unsigned, 8,  8, "u64"    } },
-
-    { Type_Kind_Basic, 0, 0, NULL,                        { Basic_Kind_Float_Unsized, Basic_Flag_Float,                  0,  0, "unsized float" } },
-    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_f32, { Basic_Kind_F32,    Basic_Flag_Float,                         4,  4, "f32"    } },
-    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_f64, { Basic_Kind_F64,    Basic_Flag_Float,                         8,  4, "f64"    } },
-
-    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_rawptr, { Basic_Kind_Rawptr, Basic_Flag_Pointer,                    POINTER_SIZE,  POINTER_SIZE, "rawptr" } },
-
-    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_i8x16, { Basic_Kind_I8X16,  Basic_Flag_SIMD,                        16, 16, "i8x16" } },
-    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_i16x8, { Basic_Kind_I16X8,  Basic_Flag_SIMD,                        16, 16, "i16x8" } },
-    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_i32x4, { Basic_Kind_I32X4,  Basic_Flag_SIMD,                        16, 16, "i32x4" } },
-    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_i64x2, { Basic_Kind_I64X2,  Basic_Flag_SIMD,                        16, 16, "i64x2" } },
-    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_f32x4, { Basic_Kind_F32X4,  Basic_Flag_SIMD,                        16, 16, "f32x4" } },
-    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_f64x2, { Basic_Kind_F64X2,  Basic_Flag_SIMD,                        16, 16, "f64x2" } },
-    { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_v128,  { Basic_Kind_V128,   Basic_Flag_SIMD,                        16, 16, "v128"  } },
-
-    { Type_Kind_Basic, 0, 0, NULL,                          { Basic_Kind_Type_Index, Basic_Flag_Type_Index,              4,  4, "type_expr" } },
-};
-
-// TODO: Document this!!
-       bh_imap type_map;
-static bh_imap type_pointer_map;
-static bh_imap type_array_map;
-static bh_imap type_slice_map;
-static bh_imap type_dynarr_map;
-static bh_imap type_vararg_map;
-static Table(u64) type_func_map;
-
-static Type* type_create(TypeKind kind, bh_allocator a, u32 extra_type_pointer_count) {
-    Type* type = bh_alloc(a, sizeof(Type) + sizeof(Type *) * extra_type_pointer_count);
-    type->kind = kind;
-    type->ast_type = NULL;
-    return type;
-}
-
-static void type_register(Type* type) {
-    static u32 next_unique_id = 1;
-    type->id = next_unique_id++;
-    if (type->ast_type) type->ast_type->type_id = type->id;
-
-    bh_imap_put(&type_map, type->id, (u64) type);
-}
-
-void types_init() {
-    bh_imap_init(&type_map,         global_heap_allocator, 255);
-    bh_imap_init(&type_pointer_map, global_heap_allocator, 255);
-    bh_imap_init(&type_array_map,   global_heap_allocator, 255);
-    bh_imap_init(&type_slice_map,   global_heap_allocator, 255);
-    bh_imap_init(&type_dynarr_map,  global_heap_allocator, 255);
-    bh_imap_init(&type_vararg_map,  global_heap_allocator, 255);
-    sh_new_arena(type_func_map);
-
-    fori (i, 0, Basic_Kind_Count) type_register(&basic_types[i]);
-}
-
-void types_dump_type_info() {
-    bh_arr_each(bh__imap_entry, entry, type_map.entries) {
-        bh_printf("%d -> %s\n", entry->key, type_get_name((Type *) entry->value));
-    }
-}
-
-b32 types_are_compatible_(Type* t1, Type* t2, b32 recurse_pointers) {
-    // NOTE: If they are pointing to the same thing,
-    // it is safe to assume they are the same type
-    if (t1 == t2) return 1;
-    if (t1 == NULL || t2 == NULL) return 0;
-    if (t1->id == t2->id) return 1;
-
-    if (t1 == &type_auto_return || t2 == &type_auto_return) {
-        return 0;
-    }
-
-    switch (t1->kind) {
-        case Type_Kind_Basic:
-            if (t2->kind == Type_Kind_Basic) {
-                // Signedness of an integer doesn't matter.
-                if ((t1->Basic.flags & Basic_Flag_Integer) && (t2->Basic.flags & Basic_Flag_Integer)) {
-                    return t1->Basic.size == t2->Basic.size;
-                }
-
-                if (t1->Basic.kind == Basic_Kind_V128 || t2->Basic.kind == Basic_Kind_V128) return 1;
-            }
-
-            if (t1->Basic.kind == Basic_Kind_Rawptr && type_is_pointer(t2)) {
-                return 1;
-            }
-            break;
-
-        case Type_Kind_Pointer: {
-            if (t2->kind == Type_Kind_Pointer) {
-                if (!recurse_pointers) return 1;
-
-                if (types_are_compatible(t1->Pointer.elem, t2->Pointer.elem)) return 1;
-
-                if (t1->Pointer.elem->kind == Type_Kind_Struct && t2->Pointer.elem->kind == Type_Kind_Struct) {
-                    Type* t1_struct = t1->Pointer.elem;
-                    Type* t2_struct = t2->Pointer.elem;
-
-                    bh_arr(StructMember *) members = t1_struct->Struct.memarr;
-                    if (bh_arr_length(members) > 0 && members[0]->used)
-                        return types_are_compatible(t2_struct,members[0]->type);
-                }
-            }
-
-            if (t2->kind == Type_Kind_Basic && t2->Basic.kind == Basic_Kind_Rawptr) return 1;
-
-            break;
-        }
-
-        case Type_Kind_Array: {
-            if (t2->kind != Type_Kind_Array) return 0;
-
-            if (t1->Array.count != 0)
-                if (t1->Array.count != t2->Array.count) return 0;
-
-            return types_are_compatible(t1->Array.elem, t2->Array.elem);
-        }
-
-        case Type_Kind_Struct: {
-            // NOTE: The check above for t1 == t2 would already catch this.
-
-            // if (t2->kind != Type_Kind_Struct) return 0;
-            // if (t1->Struct.unique_id != t2->Struct.unique_id) return 0;
-            // if (t1->Struct.mem_count != t2->Struct.mem_count) return 0;
-            return 0;
-        }
-
-        case Type_Kind_Enum: {
-            // NOTE: The check above for t1 == t2 would already catch this.
-            return 0;
-        }
-
-        case Type_Kind_Function: {
-            if (t2->kind != Type_Kind_Function) return 0;
-            if (t1->Function.param_count != t2->Function.param_count) return 0;
-
-            if (!types_are_compatible(t1->Function.return_type, t2->Function.return_type)) return 0;
-
-            if (t1->Function.param_count > 0) {
-                fori (i, 0, t1->Function.param_count) {
-                    if (!types_are_compatible(t1->Function.params[i], t2->Function.params[i])) return 0;
-                }
-            }
-
-            return 1;
-        }
-
-        case Type_Kind_Slice: {
-            if (t2->kind != Type_Kind_Slice) return 0;
-            return types_are_compatible(t1->Slice.elem, t2->Slice.elem);
-        }
-
-        case Type_Kind_VarArgs: {
-            if (t2->kind != Type_Kind_VarArgs) return 0;
-            return types_are_compatible(t1->VarArgs.elem, t2->VarArgs.elem);
-        }
-
-        case Type_Kind_DynArray: {
-            if (t2->kind != Type_Kind_DynArray) return 0;
-            return types_are_compatible(t1->DynArray.elem, t2->DynArray.elem);
-        }
-
-        case Type_Kind_Compound: {
-            if (t2->kind != Type_Kind_Compound) return 0;
-            if (t1->Compound.count != t2->Compound.count) return 0;
-
-            fori (i, 0, (i64) t1->Compound.count) {
-                if (!types_are_compatible(t1->Compound.types[i], t2->Compound.types[i])) return 0;
-            }
-
-            return 1;
-        }
-
-        case Type_Kind_Distinct:
-            // If the above cases didn't catch it, then these distinct types are not compatible.
-            return 0;
-
-        default:
-            assert(("Invalid type", 0));
-            break;
-    }
-
-    return 0;
-}
-
-b32 types_are_compatible(Type* t1, Type* t2) {
-    return types_are_compatible_(t1, t2, 1);
-}
-
-u32 type_size_of(Type* type) {
-    if (type == NULL) return 0;
-
-    switch (type->kind) {
-        case Type_Kind_Basic:    return type->Basic.size;
-        case Type_Kind_Pointer:  return POINTER_SIZE;
-        case Type_Kind_Function: return 4;
-        case Type_Kind_Array:    return type->Array.size;
-        case Type_Kind_Struct:   return type->Struct.size;
-        case Type_Kind_Enum:     return type_size_of(type->Enum.backing);
-        case Type_Kind_Slice:    return POINTER_SIZE * 2; // HACK: These should not have to be 16 bytes in size, they should only have to be 12,
-        case Type_Kind_VarArgs:  return POINTER_SIZE * 2; // but there are alignment issues right now with that so I decided to not fight it and just make them 16 bytes in size.
-        case Type_Kind_DynArray: return POINTER_SIZE + 8 + 2 * POINTER_SIZE; // data (8), count (4), capacity (4), allocator { func (4), ---(4), data (8) }
-        case Type_Kind_Compound: return type->Compound.size;
-        case Type_Kind_Distinct: return type_size_of(type->Distinct.base_type);
-        default:                 return 0;
-    }
-}
-
-u32 type_alignment_of(Type* type) {
-    if (type == NULL) return 1;
-
-    switch (type->kind) {
-        case Type_Kind_Basic:    return type->Basic.alignment;
-        case Type_Kind_Pointer:  return POINTER_SIZE;
-        case Type_Kind_Function: return 4;
-        case Type_Kind_Array:    return type_alignment_of(type->Array.elem);
-        case Type_Kind_Struct:   return type->Struct.alignment;
-        case Type_Kind_Enum:     return type_alignment_of(type->Enum.backing);
-        case Type_Kind_Slice:    return POINTER_SIZE;
-        case Type_Kind_VarArgs:  return POINTER_SIZE;
-        case Type_Kind_DynArray: return POINTER_SIZE;
-        case Type_Kind_Compound: return 4; // HACK
-        case Type_Kind_Distinct: return type_alignment_of(type->Distinct.base_type);
-        default: return 1;
-    }
-}
-
-// If this function returns NULL, then the caller MUST yield because the type may still be constructed in the future.
-// If there was an error constructing the type, then this function will report that directly.
-Type* type_build_from_ast(bh_allocator alloc, AstType* type_node) {
-    if (type_node == NULL) return NULL;
-
-    switch (type_node->kind) {
-        case Ast_Kind_Pointer_Type: {
-            // ((AstPointerType *) type_node)->elem->flags |= type_node->flags & Ast_Flag_Header_Check_No_Error;
-            Type* ptr_type = type_make_pointer(alloc, type_build_from_ast(alloc, ((AstPointerType *) type_node)->elem));
-            if (ptr_type) ptr_type->ast_type = type_node;
-            return ptr_type;
-        }
-
-        case Ast_Kind_Function_Type: {
-            AstFunctionType* ftype_node = (AstFunctionType *) type_node;
-            u64 param_count = ftype_node->param_count;
-
-            Type* return_type = type_build_from_ast(alloc, ftype_node->return_type);
-            if (return_type == NULL) return NULL;
-
-            Type* func_type = type_create(Type_Kind_Function, alloc, param_count);
-            func_type->ast_type = type_node;
-            func_type->Function.param_count = param_count;
-            func_type->Function.needed_param_count = param_count;
-            func_type->Function.vararg_arg_pos = -1;
-            func_type->Function.return_type = return_type;
-
-            if (param_count > 0) {
-                fori (i, 0, (i64) param_count) {
-                    func_type->Function.params[i] = type_build_from_ast(alloc, ftype_node->params[i]);
-
-                    // LEAK LEAK LEAK
-                    if (func_type->Function.params[i] == NULL) return NULL;
-                }
-            }
-
-            char* name = (char *) type_get_unique_name(func_type);
-            if (func_type->Function.return_type != &type_auto_return) {
-                i32 index = shgeti(type_func_map, name);
-                if (index != -1) {
-                    u64 id = type_func_map[index].value;
-                    Type* existing_type = (Type *) bh_imap_get(&type_map, id);
-
-                    // LEAK LEAK LEAK the func_type that is created
-                    return existing_type;
-                }
-            }
-
-            type_register(func_type);
-            shput(type_func_map, name, func_type->id);
-
-            return func_type;
-        }
-
-        case Ast_Kind_Array_Type: {
-            AstArrayType* a_node = (AstArrayType *) type_node;
-
-            Type *elem_type = type_build_from_ast(alloc, a_node->elem);
-            if (elem_type == NULL)  return NULL;
-
-            u32 count = 0;
-            if (a_node->count_expr) {
-                if (a_node->count_expr->type == NULL)
-                    a_node->count_expr->type = type_build_from_ast(alloc, a_node->count_expr->type_node);
-
-                if (node_is_auto_cast((AstNode *) a_node->count_expr)) {
-                    a_node->count_expr = ((AstUnaryOp *) a_node)->expr;
-                }
-
-                resolve_expression_type(a_node->count_expr);
-
-                // NOTE: Currently, the count_expr has to be an I32 literal
-                if (a_node->count_expr->type->kind != Type_Kind_Basic
-                    || a_node->count_expr->type->Basic.kind != Basic_Kind_I32) {
-                    onyx_report_error(type_node->token->pos, Error_Critical, "Array type expects type 'i32' for size, got '%s'.",
-                        type_get_name(a_node->count_expr->type));
-                    return NULL;
-                }
-
-                count = get_expression_integer_value(a_node->count_expr, NULL);
-            }
-
-            Type* array_type = type_make_array(alloc, elem_type, count);
-            if (array_type) array_type->ast_type = type_node;
-            return array_type;
-        }
-
-        case Ast_Kind_Struct_Type: {
-            AstStructType* s_node = (AstStructType *) type_node;
-            if (s_node->stcache != NULL) return s_node->stcache;
-            if (s_node->pending_type != NULL && s_node->pending_type_is_valid) return s_node->pending_type;
-            if (!s_node->ready_to_build_type) return NULL;
-
-            Type* s_type;
-            if (s_node->pending_type == NULL) {
-                s_type = type_create(Type_Kind_Struct, alloc, 0);
-                s_node->pending_type = s_type;
-
-                s_type->ast_type = type_node;
-                s_type->Struct.name = s_node->name;
-                s_type->Struct.mem_count = bh_arr_length(s_node->members);
-                s_type->Struct.meta_tags = s_node->meta_tags;
-                s_type->Struct.constructed_from = NULL;
-                s_type->Struct.status = SPS_Start;
-                type_register(s_type);
-
-                s_type->Struct.memarr = NULL;
-                sh_new_arena(s_type->Struct.members);
-                bh_arr_new(global_heap_allocator, s_type->Struct.memarr, s_type->Struct.mem_count);
-
-            } else {
-                s_type = s_node->pending_type;
-            }
-
-            s_type->Struct.poly_sln = NULL;
-
-            bh_arr_clear(s_type->Struct.memarr);
-            shfree(s_type->Struct.members);
-            sh_new_arena(s_type->Struct.members);
-
-            s_node->pending_type_is_valid = 1;
-
-            b32 is_union = s_node->is_union;
-            u32 size = 0;
-            u32 offset = 0;
-            u32 alignment = 1, mem_alignment;
-            u32 idx = 0;
-            bh_arr_each(AstStructMember *, member, s_node->members) {
-                if ((*member)->type == NULL)
-                    (*member)->type = type_build_from_ast(alloc, (*member)->type_node);
-
-                if ((*member)->type == NULL) {
-                    s_node->pending_type_is_valid = 0;
-                    return NULL;
-                }
-
-                mem_alignment = type_alignment_of((*member)->type);
-                if (mem_alignment <= 0) {
-                    onyx_report_error((*member)->token->pos, Error_Critical, "Invalid member type: %s. Has alignment %d", type_get_name((*member)->type), mem_alignment); 
-                    return NULL;
-                }
-
-                if (mem_alignment > alignment) alignment = mem_alignment;
-
-                if (!s_node->is_packed) {
-                    bh_align(offset, mem_alignment);
-                }
-
-                token_toggle_end((*member)->token);
-                if (shgeti(s_type->Struct.members, (*member)->token->text) != -1) {
-                    onyx_report_error((*member)->token->pos, Error_Critical, "Duplicate struct member, '%s'.", (*member)->token->text);
-                    return NULL;
-                }
-
-                StructMember* smem = bh_alloc_item(alloc, StructMember);
-                smem->offset = offset;
-                smem->type = (*member)->type;
-                smem->idx = idx;
-                smem->name = bh_strdup(alloc, (*member)->token->text);
-                smem->token = (*member)->token;
-                smem->initial_value = &(*member)->initial_value;
-                smem->meta_tags = (*member)->meta_tags;
-
-                smem->included_through_use = 0;
-                smem->used = (*member)->is_used;
-                smem->use_through_pointer_index = -1;
-                shput(s_type->Struct.members, (*member)->token->text, smem);
-                bh_arr_push(s_type->Struct.memarr, smem);
-                token_toggle_end((*member)->token);
-
-                u32 type_size = type_size_of((*member)->type);
-
-                if (!is_union) {
-                    offset += type_size;
-                    size = offset;
-                } else {
-                    size = bh_max(size, type_size);
-                }
-
-                idx++;
-            }
-
-            u32 min_alignment = get_expression_integer_value(s_node->min_alignment_, NULL);
-            alignment = bh_max(min_alignment, alignment);
-            if (!s_node->is_packed) {
-                bh_align(size, alignment);
-            }
-
-            u32 min_size = get_expression_integer_value(s_node->min_size_, NULL);
-            size = bh_max(min_size, size);
-
-            s_type->Struct.alignment = alignment;
-            s_type->Struct.size = size;
-
-            s_type->Struct.linear_members = NULL;
-            bh_arr_new(global_heap_allocator, s_type->Struct.linear_members, s_type->Struct.mem_count);
-            build_linear_types_with_offset(s_type, &s_type->Struct.linear_members, 0);
-
-            s_type->Struct.status = SPS_Members_Done;
-            return s_type;
-        }
-
-        case Ast_Kind_Enum_Type: {
-            AstEnumType* enum_node = (AstEnumType *) type_node;
-            if (enum_node->etcache) return enum_node->etcache;
-            if (enum_node->backing_type == NULL) return NULL;
-
-            Type* enum_type = type_create(Type_Kind_Enum, alloc, 0);
-            enum_node->etcache = enum_type;
-
-            enum_type->ast_type = type_node;
-            enum_type->Enum.backing = enum_node->backing_type;
-            enum_type->Enum.name = enum_node->name;
-            enum_type->Enum.is_flags = enum_node->is_flags;
-
-            type_register(enum_type);
-            return enum_type;
-        }
-
-        case Ast_Kind_Slice_Type: {
-            Type* slice_type = type_make_slice(alloc, type_build_from_ast(alloc, ((AstSliceType *) type_node)->elem));
-            if (slice_type) slice_type->ast_type = type_node;
-            return slice_type;
-        }
-
-        case Ast_Kind_DynArr_Type: {
-            Type* dynarr_type = type_make_dynarray(alloc, type_build_from_ast(alloc, ((AstDynArrType *) type_node)->elem));
-            if (dynarr_type) dynarr_type->ast_type = type_node;
-            return dynarr_type;
-        }
-
-        case Ast_Kind_VarArg_Type: {
-            Type* va_type = type_make_varargs(alloc, type_build_from_ast(alloc, ((AstVarArgType *) type_node)->elem));   
-            if (va_type) va_type->ast_type = type_node;
-            return va_type;
-        }
-
-        case Ast_Kind_Basic_Type: {
-            return ((AstBasicType *) type_node)->basic_type;
-        }
-
-        case Ast_Kind_Type_Alias: {
-            Type* type = type_build_from_ast(alloc, ((AstTypeAlias *) type_node)->to);
-            if (type && type->ast_type) type_node->type_id = type->id;
-            return type;
-        }
-
-        case Ast_Kind_Type_Raw_Alias:
-            return ((AstTypeRawAlias *) type_node)->to;
-
-        case Ast_Kind_Poly_Struct_Type: {
-            if (type_node->type_id != 0) return NULL;
-
-            Type* p_type = type_create(Type_Kind_PolyStruct, alloc, 0);
-            p_type->ast_type = type_node;
-            p_type->PolyStruct.name = ((AstPolyStructType *) type_node)->name;
-            p_type->PolyStruct.meta_tags = ((AstPolyStructType *) type_node)->base_struct->meta_tags;
-
-            type_register(p_type);
-            return NULL;
-        }
-
-        case Ast_Kind_Poly_Call_Type: {
-            AstPolyCallType* pc_type = (AstPolyCallType *) type_node;
-            pc_type->callee = (AstType *) strip_aliases((AstNode *) pc_type->callee);
-
-            if (!(pc_type->callee && pc_type->callee->kind == Ast_Kind_Poly_Struct_Type)) {
-                // If it is an unresolved field access or symbol, just return because an error will be printed elsewhere.
-                if (pc_type->callee->kind == Ast_Kind_Field_Access || pc_type->callee->kind == Ast_Kind_Symbol) return NULL;
-
-                onyx_report_error(pc_type->token->pos, Error_Critical, "Cannot instantiate a concrete type off of a non-polymorphic type.");
-                onyx_report_error(pc_type->callee->token->pos, Error_Critical, "Here is the type trying to be instantiated. (%s)", onyx_ast_node_kind_string(pc_type->callee->kind));
-                return NULL;
-            }
-
-            AstPolyStructType* ps_type = (AstPolyStructType *) pc_type->callee;
-
-            bh_arr(AstPolySolution) slns = NULL;
-            bh_arr_new(global_heap_allocator, slns, bh_arr_length(pc_type->params));
-            bh_arr_each(AstNode *, given, pc_type->params) {
-                if (node_is_type(*given)) {
-                    Type* param_type = type_build_from_ast(alloc, (AstType *) *given);
-
-                    // LEAK LEAK LEAK
-                    if (param_type == NULL) return NULL;
-
-                    bh_arr_push(slns, ((AstPolySolution) {
-                        .kind     = PSK_Type,
-                        .type     = param_type,
-                    }));
-                } else {
-                    bh_arr_push(slns, ((AstPolySolution) {
-                        .kind  = PSK_Value,
-                        .value = (AstTyped *) *given,
-                    }));
-                }
-            }
-
-            Type* concrete = polymorphic_struct_lookup(ps_type, slns, pc_type->token->pos, (pc_type->flags & Ast_Flag_Header_Check_No_Error) == 0);
-
-            // This should be copied in the previous function.
-            // CLEANUP: Maybe don't copy it and just use this one since it is allocated on the heap?
-            bh_arr_free(slns);
-
-            if (!concrete) return NULL;
-            if (concrete == (Type *) &node_that_signals_failure) return concrete;
-            concrete->Struct.constructed_from = (AstType *) ps_type;
-            return concrete;
-        }
-
-        case Ast_Kind_Type_Compound: {
-            AstCompoundType* ctype = (AstCompoundType *) type_node;
-
-            i64 type_count = bh_arr_length(ctype->types);
-
-            Type* comp_type = type_create(Type_Kind_Compound, alloc, type_count);
-            comp_type->Compound.size = 0;
-            comp_type->Compound.count = type_count;
-
-            fori (i, 0, type_count) {
-                assert(ctype->types[i] != NULL);
-                comp_type->Compound.types[i] = type_build_from_ast(alloc, ctype->types[i]);
-
-                // LEAK LEAK LEAK
-                if (comp_type->Compound.types[i] == NULL) return NULL;
-
-                comp_type->Compound.size += bh_max(type_size_of(comp_type->Compound.types[i]), 4);
-            }
-
-            bh_align(comp_type->Compound.size, 4);
-
-            comp_type->Compound.linear_members = NULL;
-            bh_arr_new(global_heap_allocator, comp_type->Compound.linear_members, comp_type->Compound.count);
-            build_linear_types_with_offset(comp_type, &comp_type->Compound.linear_members, 0);
-
-            type_register(comp_type);
-            return comp_type;
-        }
-
-        case Ast_Kind_Alias: {
-            AstAlias* alias = (AstAlias *) type_node;
-            return type_build_from_ast(alloc, (AstType *) alias->alias);
-        }
-
-        case Ast_Kind_Typeof: {
-            AstTypeOf* type_of = (AstTypeOf *) type_node;
-            if (type_of->resolved_type != NULL) {
-                return type_of->resolved_type;
-            }
-            
-            return NULL;
-        }
-
-        case Ast_Kind_Distinct_Type: {
-            AstDistinctType* distinct = (AstDistinctType *) type_node;
-            if (distinct->dtcache) return distinct->dtcache;
-
-            Type *base_type = type_build_from_ast(alloc, distinct->base_type);
-            if (base_type == NULL) return NULL;
-            if (base_type->kind != Type_Kind_Basic) {
-                onyx_report_error(distinct->token->pos, Error_Critical, "Distinct types can only be made out of primitive types. '%s' is not a primitive type.", type_get_name(base_type));
-                return NULL;
-            }
-
-            Type *distinct_type = type_create(Type_Kind_Distinct, alloc, 0);
-            distinct_type->Distinct.base_type = base_type;
-            distinct_type->Distinct.name = distinct->name;
-            distinct->dtcache = distinct_type;
-
-            type_register(distinct_type);
-            return distinct_type;
-        }
-    }
-
-    return NULL;
-}
-
-// CLEANUP: This needs to be merged with the very similar code from up above.
-Type* type_build_function_type(bh_allocator alloc, AstFunction* func) {
-    u64 param_count = bh_arr_length(func->params);
-
-    Type* return_type = type_build_from_ast(alloc, func->return_type);
-    if (return_type == NULL) return NULL;
-
-    Type* func_type = type_create(Type_Kind_Function, alloc, param_count);
-    func_type->Function.param_count = param_count;
-    func_type->Function.needed_param_count = 0;
-    func_type->Function.vararg_arg_pos = -1;
-    func_type->Function.return_type = return_type;
-
-    if (param_count > 0) {
-        i32 i = 0;
-        bh_arr_each(AstParam, param, func->params) {
-            if (param->default_value == NULL && param->vararg_kind == VA_Kind_Not_VA)
-                func_type->Function.needed_param_count++;
-
-            if (param->vararg_kind == VA_Kind_Untyped)
-                func_type->Function.vararg_arg_pos = i;
-
-            func_type->Function.params[i++] = param->local->type;
-        }
-    }
-
-    // CopyPaste from above in type_build_from_ast
-    char* name = (char *) type_get_unique_name(func_type);
-    if (func_type->Function.return_type != &type_auto_return) {
-        i32 index = shgeti(type_func_map, name);
-        if (index != -1) {
-            u64 id = type_func_map[index].value;
-            Type* existing_type = (Type *) bh_imap_get(&type_map, id);
-
-            // LEAK LEAK LEAK the func_type that is created
-            return existing_type;
-        }
-    }
-
-    type_register(func_type);
-    shput(type_func_map, name, func_type->id);
-
-    return func_type;
-}
-
-Type* type_build_compound_type(bh_allocator alloc, AstCompound* compound) {
-    i64 expr_count = bh_arr_length(compound->exprs);
-    fori (i, 0, expr_count) {
-        if (compound->exprs[i]->type == NULL) return NULL;
-        if (compound->exprs[i]->type->kind == Type_Kind_Basic) {
-            if (compound->exprs[i]->type->Basic.kind == Basic_Kind_Int_Unsized || compound->exprs[i]->type->Basic.kind == Basic_Kind_Float_Unsized) {
-                return NULL;
-            }
-        }
-    }
-
-    Type* comp_type = type_create(Type_Kind_Compound, alloc, expr_count);
-    comp_type->Compound.size = 0;
-    comp_type->Compound.count = expr_count;
-
-    fori (i, 0, expr_count) {
-        assert(compound->exprs[i]->type != NULL);
-        comp_type->Compound.types[i] = compound->exprs[i]->type;
-        comp_type->Compound.size += bh_max(type_size_of(comp_type->Compound.types[i]), 4);
-    }
-    
-    bh_align(comp_type->Compound.size, 4);
-    
-    comp_type->Compound.linear_members = NULL;
-    bh_arr_new(global_heap_allocator, comp_type->Compound.linear_members, comp_type->Compound.count);
-    build_linear_types_with_offset(comp_type, &comp_type->Compound.linear_members, 0);
-
-    type_register(comp_type);
-    return comp_type;
-}
-
-Type* type_build_implicit_type_of_struct_literal(bh_allocator alloc, AstStructLiteral* lit) {
-    Type* type = type_create(Type_Kind_Struct, alloc, 0);
-    type->ast_type = NULL;
-    type->Struct.name = NULL;
-    type->Struct.mem_count = bh_arr_length(lit->args.named_values);
-    type->Struct.meta_tags = NULL;
-    type->Struct.constructed_from = NULL;
-    type->Struct.status = SPS_Start;
-    type->Struct.poly_sln = NULL;
-    type_register(type);
-
-    type->Struct.memarr = NULL;
-    sh_new_arena(type->Struct.members);
-    bh_arr_new(global_heap_allocator, type->Struct.memarr, type->Struct.mem_count);
-
-    u32 size = 0;
-    u32 offset = 0;
-    u32 alignment = 1;
-    u32 idx = 0;
-    bh_arr_each(AstNamedValue *, pnv, lit->args.named_values) {
-        AstNamedValue *nv = *pnv;
-
-        Type* member_type = resolve_expression_type(nv->value);
-        if (member_type == NULL) {
-            return NULL;
-        }
-
-        u32 mem_alignment = type_alignment_of(member_type);
-        if (mem_alignment <= 0) {
-            return NULL;
-        }
-
-        alignment = bh_max(alignment, mem_alignment);
-        
-        // Should these structs be packed or not?
-        bh_align(offset, mem_alignment);
-
-        token_toggle_end(nv->token);
-        if (shgeti(type->Struct.members, nv->token->text) != -1) {
-            token_toggle_end(nv->token);
-            return NULL;
-        }
-
-        StructMember *smem = bh_alloc_item(alloc, StructMember);
-        smem->offset = offset;
-        smem->type = member_type;
-        smem->idx = idx;
-        smem->name = bh_strdup(alloc, nv->token->text);
-        smem->token = nv->token;
-        smem->initial_value = &nv->value;
-        smem->meta_tags = NULL;
-        smem->included_through_use = 0;
-        smem->used = 0;
-        smem->use_through_pointer_index = -1;
-        shput(type->Struct.members, nv->token->text, smem);
-        bh_arr_push(type->Struct.memarr, smem);
-        token_toggle_end(nv->token);
-
-        u32 type_size = type_size_of(member_type);
-        offset += type_size;
-        size = offset;
-        idx++;
-    }
-
-    type->Struct.alignment = alignment;
-    type->Struct.size = size;
-    type->Struct.linear_members = NULL;
-    bh_arr_new(global_heap_allocator, type->Struct.linear_members, type->Struct.mem_count);
-    build_linear_types_with_offset(type, &type->Struct.linear_members, 0);
-
-    type->Struct.status = SPS_Uses_Done;
-    return type;
-}
-
-Type* type_make_pointer(bh_allocator alloc, Type* to) {
-    if (to == NULL) return NULL;
-    if (to == (Type *) &node_that_signals_failure) return to;
-
-    assert(to->id > 0);
-    u64 ptr_id = bh_imap_get(&type_pointer_map, to->id);
-    if (ptr_id > 0) {
-        Type* ptr_type = (Type *) bh_imap_get(&type_map, ptr_id);
-        return ptr_type;
-
-    } else {
-        Type* ptr_type = type_create(Type_Kind_Pointer, alloc, 0);
-        ptr_type->Pointer.base.flags |= Basic_Flag_Pointer;
-        ptr_type->Pointer.base.size = POINTER_SIZE;
-        ptr_type->Pointer.elem = to;
-
-        type_register(ptr_type);
-        bh_imap_put(&type_pointer_map, to->id, ptr_type->id);
-
-        return ptr_type;
-    }
-}
-
-Type* type_make_array(bh_allocator alloc, Type* to, u32 count) {
-    if (to == NULL) return NULL;
-    if (to == (Type *) &node_that_signals_failure) return to;
-
-    assert(to->id > 0);
-    u64 key = ((((u64) to->id) << 32) | (u64) count);
-    u64 array_id = bh_imap_get(&type_array_map, key);
-    if (array_id > 0) {
-        Type* array_type = (Type *) bh_imap_get(&type_map, array_id);
-        return array_type;
-
-    } else {
-        Type* arr_type = type_create(Type_Kind_Array, alloc, 0);
-        arr_type->Array.count = count;
-        arr_type->Array.elem = to;
-        arr_type->Array.size = count * type_size_of(to);
-
-        type_register(arr_type);
-        bh_imap_put(&type_array_map, key, arr_type->id);
-
-        return arr_type;
-    }
-}
-
-Type* type_make_slice(bh_allocator alloc, Type* of) {
-    if (of == NULL) return NULL;
-    if (of == (Type *) &node_that_signals_failure) return of;
-
-    assert(of->id > 0);
-    u64 slice_id = bh_imap_get(&type_slice_map, of->id);
-    if (slice_id > 0) {
-        Type* slice_type = (Type *) bh_imap_get(&type_map, slice_id);
-        return slice_type;
-
-    } else {
-        Type* slice_type = type_create(Type_Kind_Slice, alloc, 0);
-        type_register(slice_type);
-        bh_imap_put(&type_slice_map, of->id, slice_type->id);
-
-        type_make_pointer(alloc, of);
-        slice_type->Slice.elem = of;
-
-        return slice_type;
-    }
-}
-
-Type* type_make_dynarray(bh_allocator alloc, Type* of) {
-    if (of == NULL) return NULL;
-    if (of == (Type *) &node_that_signals_failure) return of;
-
-    assert(of->id > 0);
-    u64 dynarr_id = bh_imap_get(&type_dynarr_map, of->id);
-    if (dynarr_id > 0) {
-        Type* dynarr = (Type *) bh_imap_get(&type_map, dynarr_id);
-        return dynarr;
-
-    } else {
-        Type* dynarr = type_create(Type_Kind_DynArray, alloc, 0);
-        type_register(dynarr);
-        bh_imap_put(&type_dynarr_map, of->id, dynarr->id);
-
-        type_make_pointer(alloc, of);
-        dynarr->DynArray.elem = of;
-
-        return dynarr;
-    }
-}
-
-Type* type_make_varargs(bh_allocator alloc, Type* of) {
-    if (of == NULL) return NULL;
-    if (of == (Type *) &node_that_signals_failure) return of;
-    
-    assert(of->id > 0);
-    u64 vararg_id = bh_imap_get(&type_vararg_map, of->id);
-    if (vararg_id > 0) {
-        Type* va_type = (Type *) bh_imap_get(&type_map, vararg_id);
-        return va_type;
-
-    } else {
-        Type* va_type = type_create(Type_Kind_VarArgs, alloc, 0);
-        type_register(va_type);
-        bh_imap_put(&type_vararg_map, of->id, va_type->id);
-
-        type_make_pointer(alloc, of);
-        va_type->VarArgs.elem = of;
-
-        return va_type;
-    }
-}
-
-void build_linear_types_with_offset(Type* type, bh_arr(TypeWithOffset)* pdest, u32 offset) {
-    if (type_is_structlike_strict(type)) {
-        u32 mem_count = type_structlike_mem_count(type);
-        StructMember smem = { 0 };
-        fori (i, 0, mem_count) {
-            type_lookup_member_by_idx(type, i, &smem);
-            build_linear_types_with_offset(smem.type, pdest, offset + smem.offset);
-        }
-
-    } else if (type->kind == Type_Kind_Compound) {
-        u32 elem_offset = 0;
-        fori (i, 0, type->Compound.count) {
-            build_linear_types_with_offset(type->Compound.types[i], pdest, offset + elem_offset);
-            elem_offset += bh_max(type_size_of(type->Compound.types[i]), 4);
-        }
-
-    } else {
-        bh_arr(TypeWithOffset) dest = *pdest;
-
-        TypeWithOffset two;
-        two.type = type;
-        two.offset = offset;
-        bh_arr_push(dest, two);
-
-        *pdest = dest;
-    }
-}
-
-b32 type_struct_member_apply_use(bh_allocator alloc, Type *s_type, StructMember *smem) {
-    Type* used_type = smem->type;
-
-    b32 type_is_pointer = 0;
-    if (used_type->kind == Type_Kind_Pointer) {
-        type_is_pointer = 1;
-        used_type = type_get_contained_type(used_type);
-    }
-
-    if (used_type->kind != Type_Kind_Struct) {
-        onyx_report_error(smem->token->pos, Error_Critical, "Can only use things of structure, or pointer to structure type.");
-        return 0;
-    }
-
-    if (used_type->Struct.status == SPS_Start) return 0;
-
-    bh_arr_each(StructMember*, psmem, used_type->Struct.memarr) {
-        if (shgeti(s_type->Struct.members, (*psmem)->name) != -1) {
-            onyx_report_error(smem->token->pos, Error_Critical, "Used name '%s' conflicts with existing struct member.", (*psmem)->name);
-            return 0;
-        }
-
-        StructMember* new_smem = bh_alloc_item(alloc, StructMember);
-        new_smem->type   = (*psmem)->type;
-        new_smem->name   = (*psmem)->name;
-        new_smem->meta_tags = (*psmem)->meta_tags;
-        new_smem->used = 0;
-        new_smem->included_through_use = 1;
-
-        if (type_is_pointer) {
-            new_smem->offset = (*psmem)->offset;
-            new_smem->idx = (*psmem)->idx;
-            new_smem->initial_value = NULL;
-            new_smem->use_through_pointer_index = smem->idx;
-        } else {
-            new_smem->offset = smem->offset + (*psmem)->offset;
-            new_smem->idx    = -1; // Dummy value because I don't think this is needed.
-            new_smem->initial_value = (*psmem)->initial_value;
-            new_smem->use_through_pointer_index = -1;
-        }
-
-        shput(s_type->Struct.members, (*psmem)->name, new_smem);
-    }
-
-    return 1;
-}
-
-const char* type_get_unique_name(Type* type) {
-    if (type == NULL) return "unknown";
-
-    switch (type->kind) {
-        case Type_Kind_Basic: return type->Basic.name;
-        case Type_Kind_Pointer: return bh_aprintf(global_scratch_allocator, "^%s", type_get_unique_name(type->Pointer.elem));
-        case Type_Kind_Array: return bh_aprintf(global_scratch_allocator, "[%d] %s", type->Array.count, type_get_unique_name(type->Array.elem));
-        case Type_Kind_Struct:
-            if (type->Struct.name)
-                return bh_aprintf(global_scratch_allocator, "%s@%l", type->Struct.name, type->id);
-            else
-                return bh_aprintf(global_scratch_allocator, "%s@%l", "<anonymous struct>", type->id);
-        case Type_Kind_Enum:
-            if (type->Enum.name)
-                return bh_aprintf(global_scratch_allocator, "%s@%l", type->Enum.name, type->id);
-            else
-                return bh_aprintf(global_scratch_allocator, "%s@%l", "<anonymous enum>", type->id);
-
-        case Type_Kind_Slice: return bh_aprintf(global_scratch_allocator, "[] %s", type_get_unique_name(type->Slice.elem));
-        case Type_Kind_VarArgs: return bh_aprintf(global_scratch_allocator, "..%s", type_get_unique_name(type->VarArgs.elem));
-        case Type_Kind_DynArray: return bh_aprintf(global_scratch_allocator, "[..] %s", type_get_unique_name(type->DynArray.elem));
-
-        case Type_Kind_Function: {
-            char buf[1024];
-            memset(buf, 0, 1024);
-
-            strncat(buf, "(", 1023);
-            fori (i, 0, type->Function.param_count) {
-                strncat(buf, type_get_unique_name(type->Function.params[i]), 1023);
-
-                if (i >= type->Function.needed_param_count)
-                    strncat(buf, "?", 1023);
-
-                if (i != type->Function.param_count - 1)
-                    strncat(buf, ", ", 1023);
-            }
-
-            strncat(buf, ") -> ", 1023);
-            strncat(buf, type_get_unique_name(type->Function.return_type), 1023);
-
-            return bh_aprintf(global_scratch_allocator, "%s", buf);
-        }
-
-        case Type_Kind_Compound: {
-            char buf[1024];
-            memset(buf, 0, 1024);
-
-            strncat(buf, "(", 1023);
-            fori (i, 0, type->Compound.count) {
-                strncat(buf, type_get_unique_name(type->Compound.types[i]), 1023);
-                if (i != type->Compound.count - 1)
-                    strncat(buf, ", ", 1023);
-            }
-            strncat(buf, ")", 1023);
-
-            return bh_aprintf(global_scratch_allocator, "%s", buf);
-        }
-
-        case Type_Kind_Distinct: {
-            return bh_aprintf(global_scratch_allocator, "%s@%l", type->Distinct.name, type->id);
-        }
-
-        default: return "unknown (not null)";
-    }
-}
-
-const char* type_get_name(Type* type) {
-    if (type == NULL) return "unknown";
-
-    switch (type->kind) {
-        case Type_Kind_Basic: return type->Basic.name;
-        case Type_Kind_Pointer: return bh_aprintf(global_scratch_allocator, "^%s", type_get_name(type->Pointer.elem));
-        case Type_Kind_Array: return bh_aprintf(global_scratch_allocator, "[%d] %s", type->Array.count, type_get_name(type->Array.elem));
-        case Type_Kind_Struct:
-            if (type->Struct.name)
-                return type->Struct.name;
-            else
-                return "<anonymous struct>";
-        case Type_Kind_Enum:
-            if (type->Enum.name)
-                return type->Enum.name;
-            else
-                return "<anonymous enum>";
-
-        case Type_Kind_Slice: return bh_aprintf(global_scratch_allocator, "[] %s", type_get_name(type->Slice.elem));
-        case Type_Kind_VarArgs: return bh_aprintf(global_scratch_allocator, "..%s", type_get_name(type->VarArgs.elem));
-        case Type_Kind_DynArray: return bh_aprintf(global_scratch_allocator, "[..] %s", type_get_name(type->DynArray.elem));
-
-        case Type_Kind_Function: {
-            char buf[512];
-            fori (i, 0, 512) buf[i] = 0;
-
-            strncat(buf, "(", 511);
-            fori (i, 0, type->Function.param_count) {
-                strncat(buf, type_get_name(type->Function.params[i]), 511);
-                if (i != type->Function.param_count - 1)
-                    strncat(buf, ", ", 511);
-            }
-
-            strncat(buf, ") -> ", 511);
-            strncat(buf, type_get_name(type->Function.return_type), 511);
-
-            return bh_aprintf(global_scratch_allocator, "%s", buf);
-        }
-
-        case Type_Kind_Compound: {
-            char buf[512];
-            fori (i, 0, 512) buf[i] = 0;
-
-            strncat(buf, "(", 511);
-            fori (i, 0, type->Compound.count) {
-                strncat(buf, type_get_name(type->Compound.types[i]), 511);
-                if (i != type->Compound.count - 1)
-                    strncat(buf, ", ", 511);
-            }
-            strncat(buf, ")", 511);
-
-            return bh_aprintf(global_scratch_allocator, "%s", buf);
-        }
-
-        case Type_Kind_Distinct: {
-            return bh_aprintf(global_scratch_allocator, "%s", type->Distinct.name);
-        }
-
-        default: return "unknown";
-    }
-}
-
-u32 type_get_alignment_log2(Type* type) {
-    i32 store_size = type_alignment_of(type);
-    if      (store_size == 1) return 0;
-    else if (store_size == 2) return 1;
-    else if (store_size == 4) return 2;
-    else if (store_size == 8) return 3;
-    else if (store_size == 16) return 4;
-    return 2;
-}
-
-Type* type_get_contained_type(Type* type) {
-    if (type == NULL) return NULL;
-    switch (type->kind) {
-        case Type_Kind_Pointer:  return type->Pointer.elem;
-        case Type_Kind_Array:    return type->Array.elem;
-        case Type_Kind_Slice:    return type->Slice.elem;
-        case Type_Kind_DynArray: return type->DynArray.elem;
-        case Type_Kind_VarArgs:  return type->VarArgs.elem;
-        default: return NULL;
-    }
-}
-
-static const StructMember slice_members[] = {
-    { 0,            0, NULL,                         "data",   NULL, NULL, -1, 0, 0 },
-    { POINTER_SIZE, 1, &basic_types[Basic_Kind_U32], "count",  NULL, NULL, -1, 0, 0 },
-    { POINTER_SIZE, 1, &basic_types[Basic_Kind_U32], "size",   NULL, NULL, -1, 0, 0 },
-    { POINTER_SIZE, 1, &basic_types[Basic_Kind_U32], "length", NULL, NULL, -1, 0, 0 },
-};
-
-static const StructMember array_members[] = {
-    { 0,                0, NULL,                         "data",      NULL, NULL, -1, 0, 0 },
-    { POINTER_SIZE,     1, &basic_types[Basic_Kind_U32], "count",     NULL, NULL, -1, 0, 0 },
-    { POINTER_SIZE + 4, 2, &basic_types[Basic_Kind_U32], "capacity",  NULL, NULL, -1, 0, 0 },
-    { POINTER_SIZE + 8, 3, NULL,                         "allocator", NULL, NULL, -1, 0, 0 },
-    { POINTER_SIZE,     1, &basic_types[Basic_Kind_U32], "size",      NULL, NULL, -1, 0, 0 },
-    { POINTER_SIZE,     1, &basic_types[Basic_Kind_U32], "length",    NULL, NULL, -1, 0, 0 },
-};
-
-b32 type_lookup_member(Type* type, char* member, StructMember* smem) {
-    if (type->kind == Type_Kind_Pointer) type = type->Pointer.elem;
-
-    switch (type->kind) {
-        case Type_Kind_Struct: {
-            TypeStruct* stype = &type->Struct;
-
-            i32 index = shgeti(stype->members, member);
-            if (index == -1) return 0;
-            *smem = *stype->members[index].value;
-            return 1;
-        }
-
-        case Type_Kind_VarArgs:
-        case Type_Kind_Slice: {
-            fori (i, 0, (i64) (sizeof(slice_members) / sizeof(StructMember))) {
-                if (strcmp(slice_members[i].name, member) == 0) {
-                    *smem = slice_members[i];
-                    if (smem->idx == 0) smem->type = type_make_pointer(context.ast_alloc, type->Slice.elem);
-
-                    return 1;
-                }
-            }
-            return 0;
-        }
-
-        case Type_Kind_DynArray: {
-            fori (i, 0, (i64) (sizeof(array_members) / sizeof(StructMember))) {
-                if (strcmp(array_members[i].name, member) == 0) {
-                    *smem = array_members[i];
-                    if (smem->idx == 0) smem->type = type_make_pointer(context.ast_alloc, type->DynArray.elem);
-                    if (smem->idx == 3) smem->type = type_build_from_ast(context.ast_alloc, builtin_allocator_type);
-
-                    return 1;
-                }
-            }
-            return 0;
-        }
-
-        default: return 0;
-    }
-}
-
-b32 type_lookup_member_by_idx(Type* type, i32 idx, StructMember* smem) {
-    if (type->kind == Type_Kind_Pointer) type = type->Pointer.elem;
-
-    switch (type->kind) {
-        case Type_Kind_Struct: {
-            TypeStruct* stype = &type->Struct;
-
-            if (idx > stype->mem_count) return 0;
-            *smem = *stype->memarr[idx];
-            return 1;
-        }
-
-        // HACK: This relies on the fact that the structures for Slice and VarArgs
-        // are identical.                       - brendanfh   2020/09/07
-        case Type_Kind_VarArgs:
-        case Type_Kind_Slice: {
-            if (idx > 2) return 0;
-
-            *smem = slice_members[idx];
-            if (smem->idx == 0) smem->type = type_make_pointer(context.ast_alloc, type->Slice.elem);
-
-            return 1;
-        }
-
-        case Type_Kind_DynArray: {
-            if (idx > 4) return 0;
-
-            *smem = array_members[idx];
-            if (idx == 0) smem->type = type_make_pointer(context.ast_alloc, type->DynArray.elem);
-            if (idx == 3) smem->type = type_build_from_ast(context.ast_alloc, builtin_allocator_type);
-
-            return 1;
-        }
-
-        default: return 0;
-    }
-}
-
-i32 type_linear_member_count(Type* type) {
-    switch (type->kind) {
-        case Type_Kind_Slice:
-        case Type_Kind_VarArgs:  return 2;
-        case Type_Kind_DynArray: return 5;
-        case Type_Kind_Compound: return bh_arr_length(type->Compound.linear_members);
-        case Type_Kind_Struct:   return bh_arr_length(type->Struct.linear_members);
-        default: return 1; 
-    }
-}
-
-b32 type_linear_member_lookup(Type* type, i32 idx, TypeWithOffset* two) {
-    switch (type->kind) {
-        case Type_Kind_Slice:
-        case Type_Kind_VarArgs: {
-            if (idx == 0) { 
-                two->type = type_make_pointer(context.ast_alloc, type->Slice.elem);
-                two->offset = 0;
-            }
-            if (idx == 1) {
-                two->type = &basic_types[Basic_Kind_U32];
-                two->offset = POINTER_SIZE;
-            }
-
-            return 1;
-        }
-        case Type_Kind_DynArray: {
-            if (idx == 0) { 
-                two->type = type_make_pointer(context.ast_alloc, type->DynArray.elem);
-                two->offset = 0;
-            }
-            if (idx == 1) {
-                two->type = &basic_types[Basic_Kind_U32];
-                two->offset = POINTER_SIZE;
-            }
-            if (idx == 2) {
-                two->type = &basic_types[Basic_Kind_U32];
-                two->offset = POINTER_SIZE + 4;
-            }
-            if (idx == 3 || idx == 4) {
-                Type* allocator_type = type_build_from_ast(context.ast_alloc, builtin_allocator_type);
-                type_linear_member_lookup(allocator_type, idx - 3, two);
-                two->offset += POINTER_SIZE + 8;
-            }
-
-            return 1;
-        }
-        case Type_Kind_Compound: *two = type->Compound.linear_members[idx]; return 1;
-        case Type_Kind_Struct:   *two = type->Struct.linear_members[idx];   return 1;
-
-        case Type_Kind_Distinct:
-            two->type = type->Distinct.base_type;
-            two->offset = 0;
-            return 1;
-
-        default: {
-            if (idx > 0) return 0;
-            two->offset = 0;
-            two->type = type;
-            return 1;
-        }
-    }
-}
-
-i32 type_get_idx_of_linear_member_with_offset(Type* type, u32 offset) {
-    switch (type->kind) {
-        case Type_Kind_Slice:
-        case Type_Kind_VarArgs: {
-            if (offset == 0) return 0;
-            if (offset == POINTER_SIZE) return 1;
-            return -1;
-        }
-        case Type_Kind_DynArray: {
-            if (offset == 0)                    return 0;
-            if (offset == POINTER_SIZE)         return 1;
-            if (offset == POINTER_SIZE + 4)     return 2;
-            if (offset == POINTER_SIZE + 8)     return 3;
-            if (offset == POINTER_SIZE * 2 + 8) return 4;
-            return -1;
-        }
-        case Type_Kind_Compound: {
-            i32 idx = 0;
-            bh_arr_each(TypeWithOffset, two, type->Compound.linear_members) {
-                if (two->offset == offset) return idx;
-                idx++;
-            }
-
-            return -1;
-        }
-        case Type_Kind_Struct: {
-            i32 idx = 0;
-            bh_arr_each(TypeWithOffset, two, type->Struct.linear_members) {
-                if (two->offset == offset) return idx;
-                idx++;
-            }
-
-            return -1;
-        }
-        default: return -1;
-    }
-}
-
-b32 type_struct_is_simple(Type* type) {
-    if (type->kind != Type_Kind_Struct) return 0;
-
-    b32 is_simple = 1;
-    bh_arr_each(StructMember *, mem, type->Struct.memarr) {
-        if (type_is_compound((*mem)->type) || (*mem)->type->kind == Type_Kind_Array) {
-            is_simple = 0;
-            break;
-        }
-    }
-
-    return is_simple;
-}
-
-b32 type_is_pointer(Type* type) {
-    if (type == NULL) return 0;
-    return type->kind == Type_Kind_Pointer;
-}
-
-b32 type_is_rawptr(Type* type) {
-    if (type == NULL) return 0;
-    return type->kind == Type_Kind_Basic && type->Basic.kind == Basic_Kind_Rawptr;
-}
-
-b32 type_is_array(Type* type) {
-    if (type == NULL) return 0;
-    return type->kind == Type_Kind_Array;
-}
-
-b32 type_is_struct(Type* type) {
-    if (type == NULL) return 0;
-    if (type->kind == Type_Kind_Struct) return 1;
-    if (type->kind == Type_Kind_Pointer)
-        if (type->Pointer.elem != NULL && type->Pointer.elem->kind == Type_Kind_Struct) return 1;
-    return 0;
-}
-
-b32 type_is_bool(Type* type) {
-    if (type == NULL) return 0;
-    return type != NULL && type->kind == Type_Kind_Basic && type->Basic.kind == Basic_Kind_Bool;
-}
-
-b32 type_is_small_integer(Type* type) {
-    if (type == NULL) return 0;
-    if (type->kind == Type_Kind_Enum) return type_is_small_integer(type->Enum.backing);
-    if (type->kind == Type_Kind_Distinct) return type_is_small_integer(type->Distinct.base_type);
-    if (type->kind != Type_Kind_Basic) return 0;
-
-    return type->Basic.kind >= Basic_Kind_I8 && type->Basic.kind <= Basic_Kind_U32;
-}
-
-b32 type_is_integer(Type* type) {
-    if (type == NULL) return 0;
-    if (type->kind == Type_Kind_Enum) return type_is_integer(type->Enum.backing);
-    if (type->kind == Type_Kind_Distinct) return type_is_integer(type->Distinct.base_type);
-    if (type->kind != Type_Kind_Basic) return 0;
-
-    return (type->Basic.kind >= Basic_Kind_I8 && type->Basic.kind <= Basic_Kind_U64)
-        || type->Basic.kind == Basic_Kind_Type_Index;
-}
-
-b32 type_is_numeric(Type* type) {
-    if (type == NULL) return 0;
-    if (type->kind == Type_Kind_Enum) return 1;
-    if (type->kind == Type_Kind_Distinct) return type_is_numeric(type->Distinct.base_type);
-    if (type->kind != Type_Kind_Basic) return 0;
-
-    return type->Basic.kind >= Basic_Kind_Int_Unsized && type->Basic.kind <= Basic_Kind_F64;
-}
-
-b32 type_is_compound(Type* type) {
-    if (type == NULL) return 0;
-
-    if (type->kind == Type_Kind_Struct) {
-        //
-        // This is for the kind of common case where a structure simply wraps a
-        // single non-compound value; in this situation, the structure can be
-        // "dissolved" at compile-time and turn into the underlying type.
-        //
-        
-        if (bh_arr_length(type->Struct.linear_members) != 1) return 1;
-        return type_is_compound(type->Struct.linear_members[0].type);
-    }
-
-    return type->kind != Type_Kind_Basic
-        && type->kind != Type_Kind_Pointer
-        && type->kind != Type_Kind_Enum
-        && type->kind != Type_Kind_Function
-        && type->kind != Type_Kind_Array
-        && type->kind != Type_Kind_Distinct;
-}
-
-b32 type_is_simd(Type* type) {
-    if (type == NULL) return 0;
-    if (type->kind != Type_Kind_Basic) return 0;
-    return type->Basic.flags & Basic_Flag_SIMD;
-}
-
-b32 type_results_in_void(Type* type) {
-    return (type == NULL)
-        || (type->kind == Type_Kind_Basic && type->Basic.kind == Basic_Kind_Void)
-        || (   (type->kind == Type_Kind_Function)
-            && (type->Function.return_type->kind == Type_Kind_Basic)
-            && (type->Function.return_type->Basic.kind == Basic_Kind_Void));
-}
-
-b32 type_is_array_accessible(Type* type) {
-    if (type == NULL) return 0;
-    if (type_is_pointer(type)) return 1;
-    if (type->kind == Type_Kind_Array) return 1;
-    if (type->kind == Type_Kind_Slice) return 1;
-    if (type->kind == Type_Kind_DynArray) return 1;
-    if (type->kind == Type_Kind_VarArgs) return 1;
-    return 0;
-}
-
-b32 type_is_structlike(Type* type) {
-    if (type == NULL) return 0;
-    if (type->kind == Type_Kind_Array) return 1;
-    if (type->kind == Type_Kind_Struct) return 1;
-    if (type->kind == Type_Kind_Slice)  return 1;
-    if (type->kind == Type_Kind_DynArray) return 1;
-    if (type->kind == Type_Kind_VarArgs) return 1;
-    if (type->kind == Type_Kind_Pointer) {
-        if (type->Pointer.elem->kind == Type_Kind_Struct) return 1;
-        if (type->Pointer.elem->kind == Type_Kind_Slice)  return 1;
-        if (type->Pointer.elem->kind == Type_Kind_DynArray) return 1;
-    }
-    return 0;
-}
-
-b32 type_is_structlike_strict(Type* type) {
-    if (type == NULL) return 0;
-    if (type->kind == Type_Kind_Struct)   return 1;
-    if (type->kind == Type_Kind_Slice)    return 1;
-    if (type->kind == Type_Kind_DynArray) return 1;
-    if (type->kind == Type_Kind_VarArgs)  return 1;
-    return 0;
-}
-
-u32 type_structlike_mem_count(Type* type) {
-    if (type == NULL) return 0;
-    switch (type->kind) {
-        case Type_Kind_Struct:   return type->Struct.mem_count;
-        case Type_Kind_Slice:    return 2;
-        case Type_Kind_VarArgs:  return 2;
-        case Type_Kind_DynArray: return 4;
-        default: return 0;
-    }
-}
-
-u32 type_structlike_is_simple(Type* type) {
-    if (type == NULL) return 0;
-    switch (type->kind) {
-        case Type_Kind_Struct:   return type_struct_is_simple(type);
-        case Type_Kind_Slice:    return 1;
-        case Type_Kind_VarArgs:  return 1;
-        case Type_Kind_DynArray: return 0;
-        default: return 0;
-    }
-}
-
-b32 type_is_sl_constructable(Type* type) {
-    if (type == NULL) return 0;
-    switch (type->kind) {
-        case Type_Kind_Struct:   return 1;
-        case Type_Kind_Slice:    return 1;
-        case Type_Kind_DynArray: return 1;
-        default: return 0;
-    }
-}
-
-b32 type_struct_constructed_from_poly_struct(Type* struct_type, struct AstType* from) {
-    if (struct_type == NULL) return 0;
-    if (struct_type->kind != Type_Kind_Struct) return 0;
-
-    return struct_type->Struct.constructed_from == from;
-}
diff --git a/src/utils.c b/src/utils.c
deleted file mode 100644 (file)
index 7f4ea49..0000000
+++ /dev/null
@@ -1,1206 +0,0 @@
-#define BH_DEBUG
-
-#include "utils.h"
-#include "lex.h"
-#include "astnodes.h"
-#include "errors.h"
-#include "parser.h"
-#include "astnodes.h"
-#include "errors.h"
-
-bh_scratch global_scratch;
-bh_allocator global_scratch_allocator;
-
-bh_managed_heap global_heap;
-bh_allocator global_heap_allocator;
-
-//
-// Program info and packages
-//
-Package* package_lookup(char* package_name) {
-    i32 index = shgeti(context.packages, package_name);
-    if (index != -1) {
-        return context.packages[index].value;
-    } else {
-        return NULL;
-    }
-}
-
-Package* package_lookup_or_create(char* package_name, Scope* parent_scope, bh_allocator alloc, OnyxFilePos pos) {
-    i32 index = shgeti(context.packages, package_name);
-    if (index != -1) {
-        return context.packages[index].value;
-
-    } else {
-        Package* package = bh_alloc_item(alloc, Package);
-
-        char* pac_name = bh_alloc_array(alloc, char, strlen(package_name) + 1);
-        memcpy(pac_name, package_name, strlen(package_name) + 1);
-        pac_name[strlen(package_name)] = '\0';
-
-        package->name = pac_name;
-        package->use_package_entities = NULL;
-
-        if (!strcmp(pac_name, "builtin")) {
-            package->private_scope = scope_create(alloc, context.global_scope, pos);
-            package->scope = context.global_scope;
-        } else {
-            package->scope = scope_create(alloc, parent_scope, pos);
-            package->private_scope = scope_create(alloc, package->scope, pos);
-        }
-
-        shput(context.packages, pac_name, package);
-
-        if (!charset_contains(pac_name, '.')) {
-            AstPackage* package_node = onyx_ast_node_new(alloc, sizeof(AstPackage), Ast_Kind_Package);
-            package_node->package_name = package->name;
-            package_node->package = package;
-
-            symbol_raw_introduce(context.global_scope, pac_name, pos, (AstNode *) package_node);
-        }
-
-        return package;
-    }
-}
-
-void package_track_use_package(Package* package, Entity* entity) {
-    assert(entity);
-
-    if (package->use_package_entities == NULL) {
-        bh_arr_new(global_heap_allocator, package->use_package_entities, 4);
-    }
-
-    bh_arr_push(package->use_package_entities, entity);
-}
-
-void package_reinsert_use_packages(Package* package) {
-    if (!package) return;
-    if (!package->use_package_entities) return;
-
-    bh_arr_each(Entity *, use_package, package->use_package_entities) {
-        (*use_package)->state = Entity_State_Resolve_Symbols;
-        (*use_package)->macro_attempts = 0;
-        entity_heap_insert_existing(&context.entities, *use_package);
-    } 
-
-    bh_arr_set_length(package->use_package_entities, 0);
-}
-
-
-//
-// Scoping
-//
-static u64 next_scope_id = 1;
-
-Scope* scope_create(bh_allocator a, Scope* parent, OnyxFilePos created_at) {
-    Scope* scope = bh_alloc_item(a, Scope);
-    scope->id = next_scope_id++;
-    scope->parent = parent;
-    scope->created_at = created_at;
-    scope->name = NULL;
-
-    scope->symbols = NULL;
-    sh_new_arena(scope->symbols);
-
-    return scope;
-}
-
-void scope_include(Scope* target, Scope* source, OnyxFilePos pos) {
-    fori (i, 0, shlen(source->symbols)) {
-        symbol_raw_introduce(target, source->symbols[i].key, pos, source->symbols[i].value);
-    }
-}
-
-b32 symbol_introduce(Scope* scope, OnyxToken* tkn, AstNode* symbol) {
-    token_toggle_end(tkn);
-
-    b32 ret = symbol_raw_introduce(scope, tkn->text, tkn->pos, symbol);
-
-    token_toggle_end(tkn);
-    return ret;
-}
-
-b32 symbol_raw_introduce(Scope* scope, char* name, OnyxFilePos pos, AstNode* symbol) {
-    if (strcmp(name, "_")) {
-        i32 index = shgeti(scope->symbols, name);
-        if (index != -1) {
-            AstNode *node = scope->symbols[index].value;
-            if (node != symbol) {
-                onyx_report_error(pos, Error_Critical, "Redeclaration of symbol '%s'.", name);
-
-                if (node->token) {
-                    onyx_report_error(node->token->pos, Error_Critical, "Previous declaration was here.");
-                }
-
-                return 0;
-            }
-            return 1;
-        }
-    }
-
-    shput(scope->symbols, name, symbol);
-    return 1;
-}
-
-void symbol_builtin_introduce(Scope* scope, char* sym, AstNode *node) {
-    shput(scope->symbols, sym, node);
-}
-
-void symbol_subpackage_introduce(Scope* scope, char* sym, AstPackage* package) {
-    i32 index = shgeti(scope->symbols, sym);
-    if (index != -1) {
-        AstNode* maybe_package = scope->symbols[index].value;
-        
-        // CLEANUP: Make this assertion an actual error message.
-        assert(maybe_package->kind == Ast_Kind_Package);
-    } else {
-        shput(scope->symbols, sym, (AstNode *) package);
-    }
-}
-
-AstNode* symbol_raw_resolve(Scope* start_scope, char* sym) {
-    Scope* scope = start_scope;
-
-    while (scope != NULL) {
-        i32 index = shgeti(scope->symbols, sym);
-        if (index != -1) {
-            AstNode* res = scope->symbols[index].value;
-
-            if ((res->flags & Ast_Flag_Symbol_Invisible) == 0) {
-                return res;
-            }
-        }
-
-        scope = scope->parent;
-    }
-
-    return NULL;
-}
-
-AstNode* symbol_resolve(Scope* start_scope, OnyxToken* tkn) {
-    token_toggle_end(tkn);
-    AstNode* res = symbol_raw_resolve(start_scope, tkn->text);
-    token_toggle_end(tkn);
-
-    return res;
-}
-
-AstNode* try_symbol_raw_resolve_from_node(AstNode* node, char* symbol) {
-    b32 used_pointer = 0;
-
-    while (1) {
-        if (!node) return NULL;
-
-        switch (node->kind) {
-            case Ast_Kind_Type_Raw_Alias: node = (AstNode *) ((AstTypeRawAlias *) node)->to->ast_type; break;
-            case Ast_Kind_Type_Alias:     node = (AstNode *) ((AstTypeAlias *) node)->to; break;
-            case Ast_Kind_Alias:          node = (AstNode *) ((AstAlias *) node)->alias; break;
-            case Ast_Kind_Pointer_Type: {
-                if (used_pointer) goto all_types_peeled_off;
-                used_pointer = 1;
-
-                node = (AstNode *) ((AstPointerType *) node)->elem;
-                break;
-            }
-
-            default: goto all_types_peeled_off;
-        }
-    }
-
-all_types_peeled_off:
-    if (!node) return NULL;
-
-    switch (node->kind) {
-        case Ast_Kind_Package: {
-            AstPackage* package = (AstPackage *) node;
-
-            // CLEANUP
-            if (package->package == NULL) {
-                package->package = package_lookup(package->package_name);
-            }
-
-            if (package->package == NULL) {
-                return NULL;
-            }
-
-            return symbol_raw_resolve(package->package->scope, symbol);
-        } 
-
-        case Ast_Kind_Foreign_Block: {
-            AstForeignBlock* fb = (AstForeignBlock *) node;
-
-            if (fb->scope == NULL)
-                return NULL;
-
-            return symbol_raw_resolve(fb->scope, symbol);
-        }
-
-        case Ast_Kind_Enum_Type: {
-            AstEnumType* etype = (AstEnumType *) node;
-            return symbol_raw_resolve(etype->scope, symbol);
-        }
-
-        case Ast_Kind_Struct_Type: {
-            AstStructType* stype = (AstStructType *) node;
-
-            // HACK HACK
-            // Temporarily disable the parent scope so that you can't access things
-            // "above" the structures scope. This leads to unintended behavior, as when
-            // you are accessing a static element on a structure, you don't expect to
-            // bleed to the top level scope.
-            AstNode *result = NULL;
-            if (stype->scope) {
-                Scope *tmp_parent = stype->scope->parent;
-                stype->scope->parent = NULL;
-                result = symbol_raw_resolve(stype->scope, symbol);
-                stype->scope->parent = tmp_parent;
-            }
-
-            if (result == NULL && stype->stcache != NULL) {
-                Type* struct_type = stype->stcache;
-                assert(struct_type->kind == Type_Kind_Struct);
-
-                bh_arr_each(AstPolySolution, sln, struct_type->Struct.poly_sln) {
-                    if (token_text_equals(sln->poly_sym->token, symbol)) {
-                        if (sln->kind == PSK_Type) {
-                            result = (AstNode *) sln->type->ast_type;
-                        } else {
-                            result = (AstNode *) sln->value;
-                        }
-                    }
-                }
-            }
-
-            return result;
-        }
-
-        case Ast_Kind_Poly_Struct_Type: {
-            AstStructType* stype = ((AstPolyStructType *) node)->base_struct;
-            return symbol_raw_resolve(stype->scope, symbol);
-        }
-
-        case Ast_Kind_Poly_Call_Type: {
-            AstNode* callee = (AstNode *) ((AstPolyCallType *) node)->callee;
-            return try_symbol_raw_resolve_from_node(callee, symbol);
-        }
-    }
-
-    return NULL;
-}
-
-AstNode* try_symbol_resolve_from_node(AstNode* node, OnyxToken* token) {
-    token_toggle_end(token);
-    AstNode* result = try_symbol_raw_resolve_from_node(node, token->text);
-    token_toggle_end(token);
-
-    return result;
-}
-
-AstNode* try_symbol_raw_resolve_from_type(Type *type, char* symbol) {
-    while (type->kind == Type_Kind_Pointer) {
-        type = type->Pointer.elem; 
-    }
-
-    if (type->kind == Type_Kind_Struct) {
-        if (type->Struct.poly_sln == NULL) return NULL;
-
-        bh_arr_each(AstPolySolution, sln, type->Struct.poly_sln) {
-            if (token_text_equals(sln->poly_sym->token, symbol)) {
-                if (sln->kind == PSK_Type) {
-                    AstTypeRawAlias* alias = onyx_ast_node_new(context.ast_alloc, sizeof(AstTypeRawAlias), Ast_Kind_Type_Raw_Alias);
-                    alias->type = &basic_types[Basic_Kind_Type_Index];
-                    alias->to = sln->type;
-                    return (AstNode *) alias;
-
-                } else {
-                    return (AstNode *) sln->value;
-                }
-            }
-        }
-    }
-
-    return NULL;
-}
-
-void scope_clear(Scope* scope) {
-    sh_new_arena(scope->symbols);
-}
-
-// Polymorphic procedures are in their own file to clean up this file.
-#include "polymorph.h"
-
-//
-// Overloaded Procedures
-//
-
-//
-// @Cleanup: Everything having to do with overload resolving!
-// Things that need to be available:
-//  * A copy of the arguments list that can be mutated
-//      - The named_values do not need to be copied, because they are not modified when fill_in_arguments happens.
-//      - Only values needs to have a copy available
-//      - This copy needs to be reset after checking every option
-//
-// Steps needed to check if an overload option is "the one":
-//  1. Figure out what the overload is
-//      a. If polymorphic, generate the header for the procedure only
-//  2. Place the arguments in the copy, according to the overload option
-//  3. Ensure the option has a type filled out
-//  4. For each argument
-//      a. Ensure it has a place to go (not too many arguments)
-//      b. Ensure it has a type
-//      c. Ensure the types match (currently there could be a problem if an option is attempted and doesn't work all the way that polymorphic procedures as arguments could still be solidified)
-//
-// Additional features that this needs to account for:
-//  * Resolving an overload from a list of parameter types
-//  * Resolving an overload from a TypeFunction (so an overloaded procedure can be passed as a parameter)
-//
-
-void add_overload_option(bh_arr(OverloadOption)* poverloads, u64 precedence, AstTyped* overload) {
-    bh_arr(OverloadOption) overloads = *poverloads;
-
-    i32 index = -1;
-    fori (i, 0, bh_arr_length(overloads)) {
-        if (overloads[i].precedence > precedence) {
-            index = i;
-            break;
-        }
-    }
-
-    if (index < 0) {
-        bh_arr_push(overloads, ((OverloadOption) {
-            .precedence = precedence,
-            .option     = overload,
-        }));
-
-    } else {
-        bh_arr_insertn(overloads, index, 1);
-        overloads[index].precedence = precedence;
-        overloads[index].option     = overload;
-    }
-
-    *poverloads = overloads;
-}
-
-// NOTE: The job of this function is to take a set of overloads, and traverse it to add all possible
-// overloads that are reachable. This is slightly more difficult than it may seem. In this language,
-// overloaded procedures have a strict ordering to their overloads, which determines how the correct
-// match will be found. This was not very complicated until overloaded procedures could be used as
-// overload options. This means that you could create an "infinite loop" of overloads like so:
-// 
-//  o1 :: {                 o2 :: {
-//      (...) { ... },          (...) { ... },
-//      o2                      o1
-//  }                       }
-//
-// Obviously, this is not really an infinite loop. It just means that all options are available if
-// o1 or o2 are called. The difference between o1 and o2 is the order that the overloads will be
-// searched. To build the the list of overloads, a hashmap is used to prevent the problem from being
-// O(n^2), even though n would (probably) be small. bh_imap has the useful property that it maintains
-// an "entries" array that, so long as nothing is ever removed from it, will maintain the order in
-// which entries were put into the map. This is useful because a simple recursive algorithm can
-// collect all the overloads into the map, and also use the map to provide a base case.
-void build_all_overload_options(bh_arr(OverloadOption) overloads, bh_imap* all_overloads) {
-    bh_arr_each(OverloadOption, overload, overloads) {
-        if (bh_imap_has(all_overloads, (u64) overload->option)) continue;
-
-        bh_imap_put(all_overloads, (u64) overload->option, 1);
-
-        if (overload->option->kind == Ast_Kind_Overloaded_Function) {
-            AstOverloadedFunction* sub_overload = (AstOverloadedFunction *) overload->option;
-            build_all_overload_options(sub_overload->overloads, all_overloads);
-        }
-    }
-}
-
-AstTyped* find_matching_overload_by_arguments(bh_arr(OverloadOption) overloads, Arguments* param_args) {
-    Arguments args;
-    arguments_clone(&args, param_args);
-    arguments_ensure_length(&args, bh_arr_length(args.values) + bh_arr_length(args.named_values));
-
-    // CLEANUP SPEED: This currently rebuilds the complete set of overloads every time one is looked up.
-    // This should be cached in the AstOverloadedFunction or somewhere like that.
-    bh_imap all_overloads;
-    bh_imap_init(&all_overloads, global_heap_allocator, bh_arr_length(overloads) * 2);
-    build_all_overload_options(overloads, &all_overloads);
-
-    AstTyped *matched_overload = NULL;
-
-    bh_arr_each(bh__imap_entry, entry, all_overloads.entries) {
-        AstTyped* node = (AstTyped *) strip_aliases((AstNode *) entry->key);
-        arguments_copy(&args, param_args);
-
-        AstFunction* overload = NULL;
-        switch (node->kind) {
-            case Ast_Kind_Macro:            overload = macro_resolve_header((AstMacro *) node, param_args, NULL, 0); break;
-            case Ast_Kind_Polymorphic_Proc: overload = polymorphic_proc_build_only_header((AstFunction *) node, PPLM_By_Arguments, param_args); break;
-            case Ast_Kind_Function:
-                overload = (AstFunction *) node;
-                arguments_clear_baked_flags(&args);
-                break;
-        }
-
-        // NOTE: Overload is not something that is known to be overloadable.
-        if (overload == NULL) continue;
-        if (overload == (AstFunction *) &node_that_signals_a_yield) return (AstTyped *) overload;
-        if (overload->kind != Ast_Kind_Function) continue;
-        if (overload->type == NULL) {
-            // If it was not possible to create the type for this procedure, tell the
-            // caller that this should yield and try again later.
-
-            // return and not continue because if the overload that didn't have a type will
-            // work in the future, then it has to take precedence over the other options available.
-            return (AstTyped *) &node_that_signals_a_yield;
-        }
-        assert(overload->type->kind == Type_Kind_Function);
-
-        arguments_remove_baked(&args);
-        arguments_ensure_length(&args, get_argument_buffer_size(&overload->type->Function, &args));
-
-        // NOTE: If the arguments cannot be placed successfully in the parameters list
-        if (!fill_in_arguments(&args, (AstNode *) overload, NULL, 0)) continue;
-        
-        VarArgKind va_kind;
-        TypeMatch tm = check_arguments_against_type(&args, &overload->type->Function, &va_kind, NULL, NULL, NULL);
-        if (tm == TYPE_MATCH_SUCCESS) {
-            matched_overload = node;
-            break;
-        }
-
-        if (tm == TYPE_MATCH_YIELD) {
-            return (AstTyped *) &node_that_signals_a_yield;
-        }
-    }
-
-    bh_imap_free(&all_overloads);
-    bh_arr_free(args.values);
-    return matched_overload;
-}
-
-AstTyped* find_matching_overload_by_type(bh_arr(OverloadOption) overloads, Type* type) {
-    if (type->kind != Type_Kind_Function) return NULL;
-
-    bh_imap all_overloads;
-    bh_imap_init(&all_overloads, global_heap_allocator, bh_arr_length(overloads) * 2);
-    build_all_overload_options(overloads, &all_overloads);
-
-    AstTyped *matched_overload = NULL;
-
-    bh_arr_each(bh__imap_entry, entry, all_overloads.entries) {
-        AstTyped* node = (AstTyped *) entry->key;
-        if (node->kind == Ast_Kind_Overloaded_Function) continue;
-
-        TypeMatch tm = unify_node_and_type(&node, type);
-        if (tm == TYPE_MATCH_SUCCESS) {
-            matched_overload = node;
-            break;
-        }
-
-        if (tm == TYPE_MATCH_YIELD) {
-            return (AstTyped *) &node_that_signals_a_yield;
-        }
-    }
-    
-    bh_imap_free(&all_overloads);
-    return matched_overload;
-}
-
-void report_unable_to_match_overload(AstCall* call, bh_arr(OverloadOption) overloads) {
-    char* arg_str = bh_alloc(global_scratch_allocator, 1024);
-    arg_str[0] = '\0';
-
-    bh_arr_each(AstTyped *, arg, call->args.values) {
-        strncat(arg_str, node_get_type_name(*arg), 1023);
-
-        if (arg != &bh_arr_last(call->args.values))
-            strncat(arg_str, ", ", 1023);
-    }
-
-    if (bh_arr_length(call->args.named_values) > 0) {
-        if (bh_arr_length(call->args.values) > 0) {
-            strncat(arg_str, ", ", 1023);
-        }
-
-        bh_arr_each(AstNamedValue *, named_value, call->args.named_values) { 
-            token_toggle_end((*named_value)->token);
-            strncat(arg_str, (*named_value)->token->text, 1023);
-            token_toggle_end((*named_value)->token);
-
-            strncat(arg_str, "=", 1023);
-            strncat(arg_str, node_get_type_name((*named_value)->value), 1023); // CHECK: this might say 'unknown'.
-
-            if (named_value != &bh_arr_last(call->args.named_values))
-                strncat(arg_str, ", ", 1023);
-        }
-    }
-
-    onyx_report_error(call->token->pos, Error_Critical, "Unable to match overloaded function with provided argument types: (%s)", arg_str);
-
-    bh_free(global_scratch_allocator, arg_str);
-
-    // CLEANUP SPEED: This currently rebuilds the complete set of overloads every time one is looked up.
-    // This should be cached in the AstOverloadedFunction or somewhere like that.
-    bh_imap all_overloads;
-    bh_imap_init(&all_overloads, global_heap_allocator, bh_arr_length(overloads) * 2);
-    build_all_overload_options(overloads, &all_overloads);
-
-    i32 i = 1;
-    bh_arr_each(bh__imap_entry, entry, all_overloads.entries) {
-        AstTyped* node = (AstTyped *) strip_aliases((AstNode *) entry->key);
-        onyx_report_error(node->token->pos, Error_Critical, "Here is one of the overloads. %d/%d", i++, bh_arr_length(all_overloads.entries));
-    }
-
-    bh_imap_free(&all_overloads);
-}
-
-
-//
-// Macros
-//
-//
-// TODO: Write this documentation
-void expand_macro(AstCall** pcall, AstFunction* template) {
-    AstCall* call = *pcall;
-    AstMacro* macro = (AstMacro *) call->callee;
-    assert(macro->kind == Ast_Kind_Macro);
-
-    assert(template->kind == Ast_Kind_Function);
-    assert(template->type != NULL);
-    assert(template->type->kind == Type_Kind_Function);
-
-    AstBlock* expansion = (AstBlock *) ast_clone(context.ast_alloc, template->body);
-    expansion->rules = Block_Rule_Macro;
-    expansion->scope = NULL;
-    expansion->next = call->next;
-
-    AstNode* subst = (AstNode *) expansion;
-
-    if (template->type->Function.return_type != &basic_types[Basic_Kind_Void]) {
-        AstDoBlock* doblock = (AstDoBlock *) onyx_ast_node_new(context.ast_alloc, sizeof(AstDoBlock), Ast_Kind_Do_Block);
-        doblock->token = expansion->token;
-        doblock->block = expansion;
-        doblock->type = template->type->Function.return_type;
-        doblock->next = expansion->next;
-        expansion->next = NULL;
-
-        subst = (AstNode *) doblock;
-    }
-
-    Scope* argument_scope = scope_create(context.ast_alloc, NULL, call->token->pos);
-    if (expansion->binding_scope != NULL)
-        scope_include(argument_scope, expansion->binding_scope, call->token->pos);
-    expansion->binding_scope = argument_scope;
-
-    // HACK HACK HACK This is probably very wrong. I don't know what guarentees that
-    // the paramters and arguments are going to be in the same order exactly.
-    Type *any_type = type_build_from_ast(context.ast_alloc, builtin_any_type);
-    fori (i, 0, bh_arr_length(call->args.values)) {
-        AstNode *value = (AstNode *) ((AstArgument *) call->args.values[i])->value;
-        assert(template->params[i].local->type);
-
-        Type *param_type = template->params[i].local->type;
-        if (param_type == any_type
-            || (param_type->kind == Type_Kind_VarArgs && param_type->VarArgs.elem == any_type)) {
-            onyx_report_error(macro->token->pos, Error_Critical, "Currently, macros do not support arguments of type 'any' or '..any'.");
-        }
-
-        symbol_introduce(argument_scope, template->params[i].local->token, value);
-    }
-
-    if (template->poly_scope != NULL)
-        scope_include(argument_scope, template->poly_scope, call->token->pos);
-
-    *(AstNode **) pcall = subst;
-    return;
-}
-
-AstFunction* macro_resolve_header(AstMacro* macro, Arguments* args, OnyxToken* callsite, b32 error_if_failed) {
-    switch (macro->body->kind) {
-        case Ast_Kind_Function: return (AstFunction *) macro->body;
-
-        case Ast_Kind_Polymorphic_Proc: {
-            AstFunction* pp = (AstFunction *) macro->body;
-            ensure_polyproc_cache_is_created(pp);
-
-            bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, PPLM_By_Arguments, args, callsite, error_if_failed);
-
-            if (slns == NULL) {
-                if (flag_to_yield) {
-                    flag_to_yield = 0;
-                    return (AstFunction *) &node_that_signals_a_yield;
-                }
-
-                return NULL;
-            }
-
-            return polymorphic_proc_build_only_header_with_slns(pp, slns, error_if_failed);
-        }
-
-        default: assert(("Bad macro body type.", 0));
-    }
-
-    return NULL;
-}
-
-
-//
-// Arguments resolving
-//
-static i32 lookup_idx_by_name(AstNode* provider, char* name) {
-    switch (provider->kind) {
-        case Ast_Kind_Struct_Literal: {
-            AstStructLiteral* sl = (AstStructLiteral *) provider;
-            assert(sl->type);
-
-            StructMember s;
-            if (!type_lookup_member(sl->type, name, &s)) return -1;
-            if (s.included_through_use) return -1;
-
-            return s.idx;
-        }
-
-        case Ast_Kind_Function: {
-            AstFunction* func = (AstFunction *) provider;
-
-            i32 param_idx = -1;
-            i32 idx = 0;
-            bh_arr_each(AstParam, param, func->params) {
-                if (token_text_equals(param->local->token, name)) {
-                    param_idx = idx;
-                    break;
-                }
-
-                idx++;
-            }
-
-            return param_idx;
-        }
-
-        default: return -1;
-    }
-}
-
-static AstNode* lookup_default_value_by_idx(AstNode* provider, i32 idx) {
-    switch (provider->kind) {
-        case Ast_Kind_Struct_Literal: {
-            AstStructLiteral* sl = (AstStructLiteral *) provider;
-            assert(sl->type);
-
-            if (sl->type->kind == Type_Kind_Struct) {
-                bh_arr(StructMember *) memarr = sl->type->Struct.memarr;
-                if (idx >= bh_arr_length(memarr)) return NULL;
-
-                return (AstNode *) *memarr[idx]->initial_value;
-            }
-
-            return NULL;
-        }
-
-        case Ast_Kind_Function: {
-            AstFunction* func = (AstFunction *) provider;
-
-            AstTyped* default_value = func->params[idx].default_value;
-            if (default_value == NULL) return NULL;
-
-            AstArgument* arg = make_argument(context.ast_alloc, default_value);
-            return (AstNode *) arg;
-        }
-
-        default: return NULL;
-    }
-}
-
-static i32 maximum_argument_count(AstNode* provider) {
-    switch (provider->kind) {
-        case Ast_Kind_Struct_Literal: {
-            AstStructLiteral* sl = (AstStructLiteral *) provider;
-            assert(sl->type);
-
-            return type_structlike_mem_count(sl->type);
-        }
-    }
-
-    // NOTE: This returns int_max for anything other than struct literals because the
-    // bounds checking on the arguments will be done elsewhere.
-    return 0x7fffffff;
-}
-
-static i32 non_baked_argument_count(Arguments* args) {
-    if (args->used_argument_count >= 0) return args->used_argument_count;
-
-    i32 count = 0;
-
-    bh_arr_each(AstTyped *, actual, args->values) {
-        if ((*actual)->kind != Ast_Kind_Argument) count++;
-        else if (!((AstArgument *) (*actual))->is_baked) count++;
-    }
-
-    bh_arr_each(AstNamedValue *, named_value, args->named_values) {
-        if ((*named_value)->value->kind != Ast_Kind_Argument) count++;
-        else if (!((AstArgument *) (*named_value)->value)->is_baked) count++;
-    }
-
-    args->used_argument_count = count;
-    return count;
-}
-
-i32 get_argument_buffer_size(TypeFunction* type, Arguments* args) {
-    i32 non_vararg_param_count = (i32) type->param_count;
-    if (non_vararg_param_count > 0) {
-        if (type->params[type->param_count - 1] == builtin_vararg_type_type) non_vararg_param_count--;
-        if (type->params[type->param_count - 1]->kind == Type_Kind_VarArgs)  non_vararg_param_count--;
-    }
-
-    return bh_max(non_vararg_param_count, non_baked_argument_count(args));
-}
-
-// NOTE: The values array can be partially filled out, and is the resulting array.
-// Returns if all the values were filled in.
-b32 fill_in_arguments(Arguments* args, AstNode* provider, char** err_msg, b32 insert_zero_values) {
-
-    { // Delete baked arguments
-        // :ArgumentResolvingIsComplicated
-        i32 max = bh_arr_length(args->values);
-        fori (i, 0, max) {
-            AstTyped* value = args->values[i];
-            if (value == NULL) continue;
-
-            if (value->kind == Ast_Kind_Argument) {
-                if (((AstArgument *) value)->is_baked) {
-                    i--;
-                    max--;
-                    bh_arr_deleten(args->values, i, 1);
-                }
-            }
-        }
-    }
-
-    if (args->named_values != NULL) {
-        bh_arr_each(AstNamedValue *, p_named_value, args->named_values) {
-            AstNamedValue* named_value = *p_named_value;
-
-            if (named_value->value->kind == Ast_Kind_Argument) {
-                if (((AstArgument *) named_value->value)->is_baked) {
-                    // :ArgumentResolvingIsComplicated
-                    bh_arr_set_length(args->values, bh_arr_length(args->values) - 1);
-                    continue;
-                }
-            }
-
-            token_toggle_end(named_value->token);
-            i32 idx = lookup_idx_by_name(provider, named_value->token->text);
-            if (idx == -1) {
-                if (err_msg) *err_msg = bh_aprintf(global_scratch_allocator, "'%s' is not a valid named parameter here.", named_value->token->text);
-                token_toggle_end(named_value->token);
-                return 0;
-            }
-
-            // assert(idx < bh_arr_length(args->values));
-            if (idx >= bh_arr_length(args->values)) {
-                if (err_msg) *err_msg = bh_aprintf(global_scratch_allocator, "Error placing value with name '%s' at index '%d'.", named_value->token->text, idx);
-                token_toggle_end(named_value->token);
-                return 0;
-            }
-
-            if (args->values[idx] != NULL && args->values[idx] != named_value->value) {
-                if (err_msg) *err_msg = bh_aprintf(global_scratch_allocator, "Multiple values given for parameter named '%s'.", named_value->token->text);
-                token_toggle_end(named_value->token);
-                return 0;
-            }
-
-            args->values[idx] = named_value->value;
-            token_toggle_end(named_value->token);
-        }
-    }
-
-    b32 success = 1;
-    fori (idx, 0, bh_arr_length(args->values)) {
-        if (args->values[idx] == NULL) args->values[idx] = (AstTyped *) lookup_default_value_by_idx(provider, idx);
-        if (args->values[idx] == NULL) {
-            if (insert_zero_values) {
-                assert(provider->token);
-                args->values[idx] = (AstTyped *) make_zero_value(context.ast_alloc, provider->token, NULL);
-            } else {
-                if (err_msg) *err_msg = bh_aprintf(global_scratch_allocator, "No value given for %d%s argument.", idx + 1, bh_num_suffix(idx + 1));
-                success = 0;
-            }
-        }
-    }
-
-    i32 maximum_arguments = maximum_argument_count(provider);
-    if (bh_arr_length(args->values) > maximum_arguments) {
-        if (err_msg) *err_msg = bh_aprintf(global_scratch_allocator, "Too many values provided. Expected at most %d.", maximum_arguments);
-        success = 0;
-    }
-
-    return success;
-}
-
-
-//
-// Argument checking
-//
-
-typedef enum ArgState {
-    AS_Expecting_Exact,
-    AS_Expecting_Typed_VA,
-    AS_Expecting_Untyped_VA,
-} ArgState;
-
-TypeMatch check_arguments_against_type(Arguments* args, TypeFunction* func_type, VarArgKind* va_kind,
-                                       OnyxToken* location, char* func_name, OnyxError* error) {
-    b32 permanent = location != NULL;
-    if (func_name == NULL) func_name = "UNKNOWN FUNCTION";
-
-    if (error) error->rank = Error_Critical;
-
-    bh_arr(AstArgument *) arg_arr = (bh_arr(AstArgument *)) args->values;
-    i32 arg_count = get_argument_buffer_size(func_type, args);
-
-    Type **formal_params = func_type->params;
-    Type* variadic_type = NULL;
-    i64 any_type_id = type_build_from_ast(context.ast_alloc, builtin_any_type)->id;
-
-    ArgState arg_state = AS_Expecting_Exact;
-    u32 arg_pos = 0;
-    while (1) {
-        switch (arg_state) {
-            case AS_Expecting_Exact: {
-                if (arg_pos >= func_type->param_count) goto type_checking_done;
-
-                if (formal_params[arg_pos]->kind == Type_Kind_VarArgs) {
-                    variadic_type = formal_params[arg_pos]->VarArgs.elem;
-                    arg_state = AS_Expecting_Typed_VA;
-                    continue;
-                }
-
-                if ((i16) arg_pos == func_type->vararg_arg_pos) {
-                    arg_state = AS_Expecting_Untyped_VA;
-                    continue;
-                }
-
-                if (arg_pos >= (u32) bh_arr_length(arg_arr)) goto type_checking_done;
-
-                assert(arg_arr[arg_pos]->kind == Ast_Kind_Argument);
-                TypeMatch tm = unify_node_and_type_(&arg_arr[arg_pos]->value, formal_params[arg_pos], permanent);
-                if (tm == TYPE_MATCH_YIELD) return tm;
-                if (tm == TYPE_MATCH_FAILED) {
-                    if (error != NULL) {
-                        error->pos = arg_arr[arg_pos]->token->pos;
-                        error->text = bh_aprintf(global_heap_allocator,
-                                "The procedure '%s' expects a value of type '%s' for %d%s parameter, got '%s'.",
-                                func_name,
-                                type_get_name(formal_params[arg_pos]),
-                                arg_pos + 1,
-                                bh_num_suffix(arg_pos + 1),
-                                node_get_type_name(arg_arr[arg_pos]->value));
-                    }
-                    return tm;
-                }
-
-                if (arg_arr[arg_pos]->value->type && arg_arr[arg_pos]->value->type->id != any_type_id && formal_params[arg_pos]->id == any_type_id) {
-                    resolve_expression_type(arg_arr[arg_pos]->value);
-                    if (error != NULL) {
-                        arg_arr[arg_pos]->pass_as_any = 1;
-                    }
-                }
-
-                arg_arr[arg_pos]->va_kind = VA_Kind_Not_VA;
-                break;
-            }
-
-            case AS_Expecting_Typed_VA: {
-                if (variadic_type->id == any_type_id) *va_kind = VA_Kind_Any;
-
-                if (arg_pos >= (u32) bh_arr_length(arg_arr)) goto type_checking_done;
-
-                if (variadic_type->id == any_type_id) {
-                    resolve_expression_type(arg_arr[arg_pos]->value);
-                    if (arg_arr[arg_pos]->value->type == NULL) {
-                        if (error != NULL) {
-                            error->pos = arg_arr[arg_pos]->token->pos;
-                            error->text = "Unable to resolve type of argument.";
-                        }
-                        return TYPE_MATCH_FAILED;
-                    }
-
-                    arg_arr[arg_pos]->va_kind = VA_Kind_Any; 
-                    break;
-                }
-
-                *va_kind = VA_Kind_Typed;
-
-                assert(arg_arr[arg_pos]->kind == Ast_Kind_Argument);
-                TypeMatch tm = unify_node_and_type_(&arg_arr[arg_pos]->value, variadic_type, permanent);
-                if (tm == TYPE_MATCH_YIELD) return tm;
-                if (tm == TYPE_MATCH_FAILED) {
-                    if (error != NULL) {
-                        error->pos = arg_arr[arg_pos]->token->pos,
-                        error->text = bh_aprintf(global_heap_allocator,
-                            "The procedure '%s' expects a value of type '%s' for the variadic parameter, got '%s'.",
-                            func_name,
-                            type_get_name(variadic_type),
-                            node_get_type_name(arg_arr[arg_pos]->value));
-                    }
-                    return tm;
-                }
-
-                arg_arr[arg_pos]->va_kind = VA_Kind_Typed;
-                break;
-            }
-
-            case AS_Expecting_Untyped_VA: {
-                *va_kind = VA_Kind_Untyped;
-
-                if (arg_pos >= (u32) bh_arr_length(arg_arr)) goto type_checking_done;
-
-                assert(arg_arr[arg_pos]->kind == Ast_Kind_Argument);
-                resolve_expression_type(arg_arr[arg_pos]->value);
-                if (arg_arr[arg_pos]->value->type == NULL) {
-                    if (error != NULL) {
-                        error->pos = arg_arr[arg_pos]->token->pos;
-                        error->text = "Unable to resolve type for argument.";
-                    }
-                    return TYPE_MATCH_FAILED;
-                }
-
-                arg_arr[arg_pos]->va_kind = VA_Kind_Untyped;
-                break;
-            }
-        }
-
-        arg_pos++;
-    }
-
-type_checking_done:
-    if (arg_pos < func_type->needed_param_count) {
-        if (error != NULL) {
-            error->pos = location->pos;
-            error->text = "Too few arguments to function call.";
-        }
-        return TYPE_MATCH_FAILED;
-    }
-
-    if (arg_pos < (u32) arg_count) {
-        if (error != NULL) {
-            error->pos = location->pos;
-            error->text = bh_aprintf(global_heap_allocator, "Too many arguments to function call. %d %d", arg_pos, arg_count);
-        }
-        return TYPE_MATCH_FAILED;
-    }
-
-    return TYPE_MATCH_SUCCESS;
-}
-        
-
-
-
-
-//
-// String parsing helpers
-//
-u32 char_to_base16_value(char x) {
-    if (x >= '0' && x <= '9') return (u32) (x - '0');
-    if (x >= 'A' && x <= 'F') return (u32) (x - 'A' + 10);
-    if (x >= 'a' && x <= 'f') return (u32) (x - 'a' + 10);
-    return 0xffffffff;
-}
-
-i32 string_process_escape_seqs(char* dest, char* src, i32 len) {
-    i32 total_len = 0;
-    for (i32 i = 0; i < len; i++) {
-        if (src[i] == '\\') {
-            i++;
-            switch (src[i]) {
-            case '0':  *dest++ = '\0'; total_len++; break;
-            case 'a':  *dest++ = '\a'; total_len++; break;
-            case 'b':  *dest++ = '\b'; total_len++; break;
-            case 'f':  *dest++ = '\f'; total_len++; break;
-            case 'n':  *dest++ = '\n'; total_len++; break;
-            case 't':  *dest++ = '\t'; total_len++; break;
-            case 'r':  *dest++ = '\r'; total_len++; break;
-            case 'v':  *dest++ = '\v'; total_len++; break;
-            case 'e':  *dest++ = '\e'; total_len++; break;
-            case '"':  *dest++ = '"';  total_len++; break;
-            case '\\': *dest++ = '\\'; total_len++; break;
-            case 'x': {
-                u8 ch1 = src[i + 1];
-                u8 ch2 = src[i + 2];
-                *dest++ = (i8) (char_to_base16_value(ch1) << 4 | char_to_base16_value(ch2));
-                total_len++;
-                i += 2;
-                break;
-            }
-            default:  *dest++ = '\\';
-                      *dest++ = src[i];
-                      total_len += 2;
-            }
-        } else {
-            *dest++ = src[i];
-            total_len += 1;
-        }
-    }
-
-    // NOTE: Gotta null terminate
-    *dest = 0;
-
-    return total_len;
-}
-
-static Scope **get_scope_from_node_helper(AstNode *node) {
-    b32 used_pointer = 0;
-
-    while (1) {
-        if (!node) return NULL;
-
-        switch (node->kind) {
-            case Ast_Kind_Type_Raw_Alias: node = (AstNode *) ((AstTypeRawAlias *) node)->to->ast_type; break;
-            case Ast_Kind_Type_Alias:     node = (AstNode *) ((AstTypeAlias *) node)->to; break;
-            case Ast_Kind_Alias:          node = (AstNode *) ((AstAlias *) node)->alias; break;
-            case Ast_Kind_Pointer_Type: {
-                if (used_pointer) goto all_types_peeled_off;
-                used_pointer = 1;
-
-                node = (AstNode *) ((AstPointerType *) node)->elem;
-                break;
-            }
-
-            default: goto all_types_peeled_off;
-        }
-    }
-
-all_types_peeled_off:
-    if (!node) return NULL;
-
-    switch (node->kind) {
-        case Ast_Kind_Package: {
-            AstPackage* package = (AstPackage *) node;
-            if (package->package == NULL) return NULL;
-
-            return &package->package->scope;
-        } 
-
-        case Ast_Kind_Enum_Type: {
-            AstEnumType* etype = (AstEnumType *) node;
-            return &etype->scope;
-        }
-
-        case Ast_Kind_Struct_Type: {
-            AstStructType* stype = (AstStructType *) node;
-            return &stype->scope;
-        }
-
-        case Ast_Kind_Poly_Struct_Type: {
-            AstPolyStructType* pstype = (AstPolyStructType *) node;
-            AstStructType* stype = pstype->base_struct;
-            return &stype->scope;
-        }
-    }
-
-    return NULL;
-}
-
-Scope *get_scope_from_node(AstNode *node) {
-    if (!node) return NULL;
-
-    Scope **pscope = get_scope_from_node_helper(node);
-    if (!pscope) return NULL;
-    return *pscope;
-}
-
-Scope *get_scope_from_node_or_create(AstNode *node) {
-    if (!node) return NULL;
-
-    Scope **pscope = get_scope_from_node_helper(node);
-    if (!pscope) return NULL;
-
-    // Create the scope if it does not exist.
-    // This uses a NULL parent, which I think is what 
-    // is used in other parts of the compiler for struct/enum
-    // scopes?
-    if (!*pscope) {
-        assert(node->token);
-        *pscope = scope_create(context.ast_alloc, NULL, node->token->pos);
-    }
-
-    return *pscope;
-}
-
-u32 levenshtein_distance(const char *str1, const char *str2) {
-    i32 m = strlen(str1) + 1;
-    i32 n = strlen(str2) + 1;
-
-    i32 *d = bh_alloc_array(global_scratch_allocator, i32, m * n);
-    fori (i, 0, m * n) d[i] = 0;
-
-    fori (i, 0, m) d[i * n + 0] = i;
-    fori (j, 0, n) d[0 * n + j] = j;
-
-    fori (j, 1, n) {
-        fori (i, 1, m) {
-            i32 subst_cost = str1[i - 1] == str2[j - 1] ? 0 : 1;
-
-            i32 a = d[(i - 1) * n + j] + 1;
-            i32 b = d[i * n + (j - 1)] + 1;
-            i32 c = d[(i - 1) * n + (j - 1)] + subst_cost;
-
-            d[i * n + j] = bh_min(bh_min(a, b), c);
-        }
-    }
-
-    return d[m * n - 1];
-}
-
-char *find_closest_symbol_in_scope(Scope *scope, char *sym, u32 *out_distance) {
-    *out_distance = 0x7fffffff;
-
-    if (scope == NULL) return NULL;
-
-    char* closest = NULL;
-    fori (i, 0, shlen(scope->symbols)) {
-        if (scope->symbols[i].value->flags & Ast_Flag_Symbol_Invisible) continue;
-
-        char *key = scope->symbols[i].key;
-        u32 d = levenshtein_distance(key, sym); 
-        if (d < *out_distance) {
-            *out_distance = d;
-            closest = (char *) key;
-        }
-    }
-
-    return closest;
-}
-
-char *find_closest_symbol_in_scope_and_parents(Scope *scope, char *sym) {
-    u32 min_dist = 0x7fffffff;
-    u32 tmp_dist; 
-
-    char *closest = NULL;
-    while (scope != NULL) {
-        char *tmp_closest = find_closest_symbol_in_scope(scope, sym, &tmp_dist);
-        if (tmp_dist < min_dist) {
-            min_dist = tmp_dist;
-            closest = tmp_closest;
-        }
-
-        scope = scope->parent;
-    }
-
-    return closest;
-}
-        
-char *find_closest_symbol_in_node(AstNode* node, char *sym) {
-    Scope *scope = get_scope_from_node(node);
-    if (!scope) {
-        if (node->kind == Ast_Kind_Poly_Call_Type) {
-            AstPolyCallType* pcall = (AstPolyCallType *) node;
-            return find_closest_symbol_in_node((AstNode *) pcall->callee, sym);
-        }
-
-        return NULL;
-    }
-
-    u32 dist;
-    return find_closest_symbol_in_scope(scope, sym, &dist);
-}
-
diff --git a/src/wasm_emit.c b/src/wasm_emit.c
deleted file mode 100644 (file)
index 0dc1c4a..0000000
+++ /dev/null
@@ -1,4588 +0,0 @@
-//
-// There are several things I'm seeing in this file that I want to clean up.
-// They are:
-//  [x] remove the need to know if the stack is needed before generating the function.
-//      Just leave 5 nops at the beginning because they will be automatically removed
-//      by the WASM outputter.
-//  [x] remove the need to have "allocate_exprs" on blocks and in functions. This will
-//      be easy once the above is done.
-//  [x] there should be a better way to emit pending deferred statements because there
-//      is some code duplication between emit_return and emit_structured_jump.
-//  [ ] Change the calling convention so it is easier to use from both JS and in the compiler.
-
-
-
-
-#define BH_DEBUG
-#include "wasm_emit.h"
-#include "utils.h"
-
-#define WASM_TYPE_INT32   0x7F
-#define WASM_TYPE_INT64   0x7E
-#define WASM_TYPE_FLOAT32 0x7D
-#define WASM_TYPE_FLOAT64 0x7C
-#define WASM_TYPE_VAR128  0x7B
-#define WASM_TYPE_PTR     WASM_TYPE_INT32
-#define WASM_TYPE_FUNC    WASM_TYPE_INT32
-#define WASM_TYPE_VOID    0x00
-
-static WasmType onyx_type_to_wasm_type(Type* type) {
-    if (type->kind == Type_Kind_Struct) {
-        if (type_linear_member_count(type) == 1) {
-            return onyx_type_to_wasm_type(type->Struct.linear_members[0].type);
-        }
-        
-        return WASM_TYPE_VOID;
-    }
-
-    if (type->kind == Type_Kind_Slice) {
-        return WASM_TYPE_VOID;
-    }
-
-    if (type->kind == Type_Kind_Compound) {
-        return WASM_TYPE_VOID;
-    }
-
-    if (type->kind == Type_Kind_Enum) {
-        return onyx_type_to_wasm_type(type->Enum.backing);
-    }
-
-    if (type->kind == Type_Kind_Distinct) {
-        return onyx_type_to_wasm_type(type->Distinct.base_type);
-    }
-
-    if (type->kind == Type_Kind_Pointer) {
-        return WASM_TYPE_PTR;
-    }
-
-    if (type->kind == Type_Kind_Array) {
-        return WASM_TYPE_PTR;
-    }
-
-    if (type->kind == Type_Kind_Function) {
-        return WASM_TYPE_FUNC;
-    }
-
-    if (type->kind == Type_Kind_Basic) {
-        TypeBasic* basic = &type->Basic;
-        if (basic->flags & Basic_Flag_Boolean) return WASM_TYPE_INT32;
-        if (basic->flags & Basic_Flag_Integer) {
-            if (basic->size <= 4) return WASM_TYPE_INT32;
-            if (basic->size == 8) return WASM_TYPE_INT64;
-        }
-        if (basic->flags & Basic_Flag_Pointer) return WASM_TYPE_INT32;
-        if (basic->flags & Basic_Flag_Float) {
-            if (basic->size <= 4) return WASM_TYPE_FLOAT32;
-            if (basic->size == 8) return WASM_TYPE_FLOAT64;
-        }
-        if (basic->flags & Basic_Flag_SIMD) return WASM_TYPE_VAR128;
-        if (basic->flags & Basic_Flag_Type_Index) return WASM_TYPE_INT32;
-        if (basic->size == 0) return WASM_TYPE_VOID;
-    }
-
-    return WASM_TYPE_VOID;
-}
-
-static i32 generate_type_idx(OnyxWasmModule* mod, Type* ft);
-static i32 get_element_idx(OnyxWasmModule* mod, AstFunction* func);
-
-#define LOCAL_I32  0x000000000
-#define LOCAL_I64  0x100000000
-#define LOCAL_F32  0x300000000
-#define LOCAL_F64  0x700000000
-#define LOCAL_V128 0xf00000000
-
-static b32 local_is_wasm_local(AstTyped* local) {
-    if (local->kind == Ast_Kind_Local && local->flags & Ast_Flag_Address_Taken) return 0;
-    if (local->type->kind == Type_Kind_Basic) return 1;
-    if (local->type->kind == Type_Kind_Enum && local->type->Enum.backing->kind == Type_Kind_Basic) return 1;
-    if (local->type->kind == Type_Kind_Distinct && local->type->Distinct.base_type->kind == Type_Kind_Basic) return 1;
-    if (local->type->kind == Type_Kind_Pointer) return 1;
-    return 0;
-}
-
-static u64 local_raw_allocate(LocalAllocator* la, WasmType wt) {
-    i32 idx = 0;
-    if (wt == WASM_TYPE_INT32)   idx = 0;
-    if (wt == WASM_TYPE_INT64)   idx = 1;
-    if (wt == WASM_TYPE_FLOAT32) idx = 2;
-    if (wt == WASM_TYPE_FLOAT64) idx = 3;
-    if (wt == WASM_TYPE_VAR128)  idx = 4;
-
-    u64 flag_bits = LOCAL_IS_WASM;
-    if (wt == WASM_TYPE_INT32)   flag_bits |= LOCAL_I32;
-    if (wt == WASM_TYPE_INT64)   flag_bits |= LOCAL_I64;
-    if (wt == WASM_TYPE_FLOAT32) flag_bits |= LOCAL_F32;
-    if (wt == WASM_TYPE_FLOAT64) flag_bits |= LOCAL_F64;
-    if (wt == WASM_TYPE_VAR128)  flag_bits |= LOCAL_V128;
-
-    if (la->freed[idx] > 0) {
-        la->freed[idx]--;
-        return flag_bits | ((u64) (la->allocated[idx] - la->freed[idx] - 1 + la->param_count));
-
-    } else {
-        la->allocated[idx]++;
-        return flag_bits | ((u64) (la->allocated[idx] - 1 + la->param_count));
-    }
-}
-
-static void local_raw_free(LocalAllocator* la, WasmType wt) {
-    i32 idx = 0;
-
-    if (wt == WASM_TYPE_INT32)   idx = 0;
-    if (wt == WASM_TYPE_INT64)   idx = 1;
-    if (wt == WASM_TYPE_FLOAT32) idx = 2;
-    if (wt == WASM_TYPE_FLOAT64) idx = 3;
-    if (wt == WASM_TYPE_VAR128)  idx = 4;
-
-    assert(la->allocated[idx] > 0 && la->freed[idx] < la->allocated[idx]);
-
-    la->freed[idx]++;
-}
-
-static u64 local_allocate(LocalAllocator* la, AstTyped* local) {
-    if (local_is_wasm_local(local)) {
-        WasmType wt = onyx_type_to_wasm_type(local->type);
-        return local_raw_allocate(la, wt);
-
-    } else {
-        u32 size = type_size_of(local->type);
-        u32 alignment = type_alignment_of(local->type);
-
-        bh_align(la->curr_stack, alignment);
-
-        if (la->max_stack < la->curr_stack)
-            la->max_stack = la->curr_stack;
-
-        bh_align(size, alignment);
-
-        if (la->max_stack - la->curr_stack >= (i32) size) {
-            la->curr_stack += size;
-
-        } else {
-            la->max_stack += size - (la->max_stack - la->curr_stack);
-            la->curr_stack = la->max_stack;
-        }
-
-        return la->curr_stack - size;
-    }
-}
-
-static void local_free(LocalAllocator* la, AstTyped* local) {
-    if (local_is_wasm_local(local)) {
-        WasmType wt = onyx_type_to_wasm_type(local->type);
-        local_raw_free(la, wt);
-
-    } else {
-        u32 size = type_size_of(local->type);
-        u32 alignment = type_alignment_of(local->type);
-        bh_align(size, alignment);
-
-        la->curr_stack -= size;
-    }
-}
-
-static u64 local_lookup_idx(LocalAllocator* la, u64 value) {
-    assert(value & LOCAL_IS_WASM);
-
-    u32 idx = value & 0xFFFFFFFF;
-    if (value & 0x100000000) idx += la->allocated[0];
-    if (value & 0x200000000) idx += la->allocated[1];
-    if (value & 0x400000000) idx += la->allocated[2];
-    if (value & 0x800000000) idx += la->allocated[3];
-
-    return (u64) idx;
-}
-
-
-static inline b32 should_emit_function(AstFunction* fd) {
-    // NOTE: Don't output intrinsic functions
-    if (fd->is_intrinsic) return 0;
-
-    // NOTE: Don't output functions that are not used, only if
-    // they are also not exported.
-    if ((fd->flags & Ast_Flag_Function_Used) == 0) {
-        if (fd->is_exported || bh_arr_length(fd->tags) > 0) {
-            return 1;
-        } else {
-            return 0;
-        }
-    }
-
-    return 1;
-}
-
-
-//
-// Debug Info Generation
-//
-
-#ifdef ENABLE_DEBUG_INFO
-
-static u32 debug_introduce_symbol(OnyxWasmModule *mod, OnyxToken *token, DebugSymLoc loc, u64 num, Type* type) {
-
-    u32 id = mod->debug_context->next_sym_id++;
-
-    DebugSymInfo sym_info;
-    sym_info.sym_id = id;
-    sym_info.location_type = loc;
-    sym_info.location_num  = num;
-    sym_info.type          = type->id;
-
-    if (token) {
-        token_toggle_end(token);
-        sym_info.name = bh_strdup(context.ast_alloc, token->text);
-        token_toggle_end(token);
-    } else {
-        sym_info.name = NULL;
-    }
-
-    bh_arr_push(mod->debug_context->sym_info, sym_info);
-
-    if (loc == DSL_REGISTER) {
-        assert(mod->local_alloc);
-        DebugSymPatch patch;
-        patch.func_idx = mod->current_func_idx;
-        patch.sym_id = id;
-        patch.local_idx = num;
-        bh_arr_push(mod->debug_context->sym_patches, patch);
-    }
-
-    bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_SYM);
-    u32 leb_len=0;
-    u8 *bytes = uint_to_uleb128(id, &leb_len);
-    bh_buffer_append(&mod->debug_context->op_buffer, bytes, leb_len);
-
-    mod->debug_context->last_op_was_rep = 0;
-    return id;
-}
-
-static u32 debug_get_file_id(OnyxWasmModule *mod, const char *name) {
-    assert(mod && mod->debug_context);
-
-    i32 index = shgeti(mod->debug_context->file_info, name);
-    if (index == -1) {
-        u32 id = mod->debug_context->next_file_id++;
-        DebugFileInfo file_info;
-        file_info.file_id = id;
-
-        bh_arr_each(bh_file_contents, fc, context.loaded_files) {
-            if (!strcmp(fc->filename, name)) {
-                file_info.line_count = fc->line_count;
-            }
-        }
-        shput(mod->debug_context->file_info, name, file_info);
-
-        return id;
-    }
-
-    return mod->debug_context->file_info[index].value.file_id;
-}
-
-static void debug_set_position(OnyxWasmModule *mod, OnyxToken *token) {
-    i32 file_id = debug_get_file_id(mod, token->pos.filename);
-
-    bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_SET);
-    mod->debug_context->last_op_was_rep = 0;
-
-    u32 leb_len=0;
-    u8 *bytes = uint_to_uleb128(file_id, &leb_len);
-    bh_buffer_append(&mod->debug_context->op_buffer, bytes, leb_len);
-
-    bytes = uint_to_uleb128(token->pos.line, &leb_len);
-    bh_buffer_append(&mod->debug_context->op_buffer, bytes, leb_len);
-
-    mod->debug_context->last_token = token;
-}
-
-// Called for every instruction being emitted
-// This has to emit either:
-//    - INC
-//    - DEC
-//    - REP
-//    - SET, REP 0
-static void debug_emit_instruction(OnyxWasmModule *mod, OnyxToken *token) {
-    DebugContext *ctx = mod->debug_context; 
-    assert(ctx && ctx->last_token);
-
-    // Sanity check
-    if (ctx->last_op_was_rep) {
-        assert((ctx->op_buffer.data[ctx->op_buffer.length - 1] & DOT_REP) == DOT_REP);
-    }
-    i32 file_id, old_file_id;
-
-    b32 repeat_previous = 0;
-    if (!token || !token->pos.filename) {
-        repeat_previous = 1;
-
-    } else {
-        file_id = debug_get_file_id(mod, token->pos.filename);
-        old_file_id = debug_get_file_id(mod, ctx->last_token->pos.filename);
-        if (old_file_id == file_id && token->pos.line == ctx->last_token->pos.line) {
-            repeat_previous = 1;
-        }
-    }
-    
-    if (repeat_previous) {
-        // Output / increment REP instruction
-        if (ctx->last_op_was_rep) {
-            u8 rep_count = ctx->op_buffer.data[ctx->op_buffer.length - 1] & 0b00111111;
-            if (rep_count != 63) {
-                rep_count += 1;
-                ctx->op_buffer.data[ctx->op_buffer.length - 1] = DOT_REP | rep_count;
-                return;
-            }
-        }
-
-        bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_REP);
-        ctx->last_op_was_rep = 1;
-        return;
-    }
-
-    // At this point, token is definitely non-null.
-    ctx->last_op_was_rep = 0;
-
-    if (old_file_id == file_id) {
-        // We see if we can INC/DEC to get to the line number
-        if (ctx->last_token->pos.line < token->pos.line) {
-            u32 diff = token->pos.line - ctx->last_token->pos.line;
-            if (diff <= 64) {
-                bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_INC | (diff - 1));
-                goto done;
-            }
-        }
-
-        if (ctx->last_token->pos.line > token->pos.line) {
-            u32 diff = ctx->last_token->pos.line - token->pos.line;
-            if (diff <= 64) {
-                bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_DEC | (diff - 1));
-                goto done;
-            }
-        }
-    }
-
-    // Otherwise, we need to output a SET, followed by a REP 0,
-    // which is what set_position does.
-    debug_set_position(mod, token);
-    bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_REP);
-    ctx->last_op_was_rep = 1;
-
-  done:
-    ctx->last_token = token;
-}
-
-static void debug_begin_function(OnyxWasmModule *mod, u32 func_idx, OnyxToken *token, char *name) {
-    u32 file_id = debug_get_file_id(mod, token->pos.filename);
-    u32 line    = token->pos.line;
-
-    assert(mod->debug_context);
-
-    DebugFuncContext func;
-    func.func_index = func_idx;
-    func.file_id = file_id;
-    func.line    = line;
-    func.op_offset = mod->debug_context->op_buffer.length;
-    func.stack_ptr_idx = 0;
-    func.name_length = strlen(name);
-    func.name = name;
-    bh_arr_push(mod->debug_context->funcs, func);
-
-    bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_PUSHF);
-    debug_set_position(mod, token);
-}
-
-static void debug_end_function(OnyxWasmModule *mod) {
-    bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_POPF);
-    bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_END);
-    mod->debug_context->last_op_was_rep = 0;
-    mod->debug_context->last_token = NULL;
-}
-
-static void debug_enter_symbol_frame(OnyxWasmModule *mod) {
-    bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_PUSHF);
-    mod->debug_context->last_op_was_rep = 0;
-}
-
-static void debug_leave_symbol_frame(OnyxWasmModule *mod) {
-    bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_POPF);
-    mod->debug_context->last_op_was_rep = 0;
-}
-
-#else
-
-#define debug_introduce_symbol(mod, name, loc, num, type) (void)0
-#define debug_get_file_id(mod, name) (void)0
-#define debug_set_position(mod, token) (void)0
-#define debug_emit_instruction(mod, token) (void)0
-#define debug_begin_function(mod, idx, token, name) (void)0
-#define debug_end_function(mod) (void)0
-#define debug_enter_symbol_frame(mod) (void)0
-#define debug_leave_symbol_frame(mod) (void)0
-
-#endif
-
-
-typedef enum StructuredBlockType StructuredBlockType;
-enum StructuredBlockType {
-    SBT_Basic_Block,       // Cannot be targeted using jump
-    SBT_Breakable_Block,   // Targeted using break
-    SBT_Continue_Block,    // Targeted using continue
-    SBT_Fallthrough_Block, // Targeted using fallthrough
-    SBT_Return_Block,      // Targeted using return, (used for expression macros)
-
-    SBT_Basic_If,          // Cannot be targeted using jump
-    SBT_Breakable_If,      // Targeted using break
-
-    SBT_Basic_Loop,        // Cannot be targeted using jump
-    SBT_Continue_Loop,     // Targeted using continue
-
-    SBT_Count,
-};
-
-#ifdef ENABLE_DEBUG_INFO
-    #define WI(token, instr) (debug_emit_instruction(mod, token),        bh_arr_push(code, ((WasmInstruction){ instr, 0x00 })))
-    #define WID(token, instr, data) (debug_emit_instruction(mod, token), bh_arr_push(code, ((WasmInstruction){ instr, data })))
-    #define WIL(token, instr, data) (debug_emit_instruction(mod, token), bh_arr_push(code, ((WasmInstruction){ instr, { .l = data } })))
-    #define WIP(token, instr, data) (debug_emit_instruction(mod, token), bh_arr_push(code, ((WasmInstruction){ instr, { .p = data } })))
-    #define WIR(token, full_instr)  (debug_emit_instruction(mod, token), bh_arr_push(code, full_instr))
-#else
-    #define WI(token, instr) (bh_arr_push(code, ((WasmInstruction){ instr, 0x00 })))
-    #define WID(token, instr, data) (bh_arr_push(code, ((WasmInstruction){ instr, data })))
-    #define WIL(token, instr, data) (bh_arr_push(code, ((WasmInstruction){ instr, { .l = data } })))
-    #define WIP(token, instr, data) (bh_arr_push(code, ((WasmInstruction){ instr, { .p = data } })))
-    #define WIR(token, full_instr)  (bh_arr_push(code, full_instr))
-#endif
-
-#define EMIT_FUNC(kind, ...) static void emit_ ## kind (OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, __VA_ARGS__)
-#define EMIT_FUNC_NO_ARGS(kind) static void emit_ ## kind (OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode)
-#define STACK_SWAP(token, type1, type2) { \
-    u64 t0 = local_raw_allocate(mod->local_alloc, type1); \
-    u64 t1 = local_raw_allocate(mod->local_alloc, type2); \
-                                                          \
-    WIL(token, WI_LOCAL_SET, t0);                         \
-    WIL(token, WI_LOCAL_SET, t1);                         \
-    WIL(token, WI_LOCAL_GET, t0);                         \
-    WIL(token, WI_LOCAL_GET, t1);                         \
-                                                          \
-    local_raw_free(mod->local_alloc, type1);              \
-    local_raw_free(mod->local_alloc, type2);              \
-    }
-#define SUBMIT_PATCH(patch_arr, offset) bh_arr_push((patch_arr), ((PatchInfo) { bh_arr_length(code) - offset }))
-#define NEXT_DATA_ID(mod)               ((u32) bh_arr_length((mod)->data) + 1)
-
-EMIT_FUNC(function_body,                 AstFunction* fd);
-EMIT_FUNC(block,                         AstBlock* block, b32 generate_block_headers);
-EMIT_FUNC(statement,                     AstNode* stmt);
-EMIT_FUNC(local_allocation,              AstTyped* stmt);
-EMIT_FUNC_NO_ARGS(free_local_allocations);
-EMIT_FUNC(data_relocation,               u32 data_id);
-EMIT_FUNC(assignment,                    AstBinaryOp* assign);
-EMIT_FUNC(assignment_of_array,           AstTyped* left, AstTyped* right);
-EMIT_FUNC(compound_assignment,           AstBinaryOp* assign);
-EMIT_FUNC(store_instruction,             Type* type, u32 offset);
-EMIT_FUNC(load_instruction,              Type* type, u32 offset);
-EMIT_FUNC(if,                            AstIfWhile* if_node);
-EMIT_FUNC(while,                         AstIfWhile* while_node);
-EMIT_FUNC(for,                           AstFor* for_node);
-EMIT_FUNC(switch,                        AstSwitch* switch_node);
-EMIT_FUNC(defer,                         AstDefer* defer);
-EMIT_FUNC(defer_code,                    WasmInstruction* deferred_code, u32 code_count);
-EMIT_FUNC(deferred_stmt,                 DeferredStmt deferred_stmt);
-EMIT_FUNC_NO_ARGS(deferred_stmts);
-EMIT_FUNC(remove_directive,              AstDirectiveRemove* remove);
-EMIT_FUNC(binop,                         AstBinaryOp* binop);
-EMIT_FUNC(unaryop,                       AstUnaryOp* unop);
-EMIT_FUNC(call,                          AstCall* call);
-EMIT_FUNC(intrinsic_call,                AstCall* call);
-EMIT_FUNC(subscript_location,            AstSubscript* sub, u64* offset_return);
-EMIT_FUNC(field_access_location,         AstFieldAccess* field, u64* offset_return);
-EMIT_FUNC(local_location,                AstLocal* local, u64* offset_return);
-EMIT_FUNC(memory_reservation_location,   AstMemRes* memres);
-EMIT_FUNC(location_return_offset,        AstTyped* expr, u64* offset_return);
-EMIT_FUNC(location,                      AstTyped* expr);
-EMIT_FUNC(compound_load,                 Type* type, u64 offset);
-EMIT_FUNC(struct_lval,                   AstTyped* lval);
-EMIT_FUNC(struct_literal,                AstStructLiteral* sl);
-EMIT_FUNC(compound_store,                Type* type, u64 offset, b32 location_first);
-EMIT_FUNC(array_store,                   Type* type, u32 offset);
-EMIT_FUNC(array_literal,                 AstArrayLiteral* al);
-EMIT_FUNC(range_literal,                 AstRangeLiteral* range);
-EMIT_FUNC(if_expression,                 AstIfExpression* if_expr);
-EMIT_FUNC(do_block,                      AstDoBlock* doblock);
-EMIT_FUNC(expression,                    AstTyped* expr);
-EMIT_FUNC(cast,                          AstUnaryOp* cast);
-EMIT_FUNC(return,                        AstReturn* ret);
-EMIT_FUNC(stack_enter,                   u64 stacksize);
-EMIT_FUNC(zero_value,                    WasmType wt);
-EMIT_FUNC(zero_value_for_type,           Type* type, OnyxToken* where);
-
-EMIT_FUNC(enter_structured_block,        StructuredBlockType sbt, OnyxToken* block_token);
-EMIT_FUNC_NO_ARGS(leave_structured_block);
-
-static u32 emit_data_entry(OnyxWasmModule *mod, WasmDatum *datum);
-
-static void emit_constexpr(ConstExprContext *ctx, AstTyped *node, u32 offset);
-static b32 emit_constexpr_(ConstExprContext *ctx, AstTyped *node, u32 offset);
-
-#include "wasm_intrinsics.h"
-#include "wasm_type_table.h"
-
-EMIT_FUNC(function_body, AstFunction* fd) {
-    if (fd->body == NULL) return;
-
-    emit_block(mod, pcode, fd->body, 0);
-}
-
-EMIT_FUNC(block, AstBlock* block, b32 generate_block_headers) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    generate_block_headers = generate_block_headers && (block->rules & Block_Rule_Emit_Instructions);
-
-    if (generate_block_headers) {
-        emit_enter_structured_block(mod, &code, (block->rules & Block_Rule_Override_Return)
-                                                ? SBT_Return_Block
-                                                : SBT_Breakable_Block,
-                                                block->token);
-        debug_enter_symbol_frame(mod);
-    }
-
-    forll (AstNode, stmt, block->body, next) {
-        emit_statement(mod, &code, stmt);
-    }
-
-    // HACK: I'm not convinced this is the best way to handle this case. Essentially, if
-    // a deferred statement uses a local that is freed from the a macro block, then the
-    // local won't be valid anymore and could have garbage data. This ensures that the
-    // freeing of the local variables and flushing of the deferred statements happen together,
-    // so a deferred statement can never try to use a local that has been freed.
-    //                                                                   - brendanfh 2021/08/29
-    if (block->rules & Block_Rule_Clear_Defer) {
-        emit_deferred_stmts(mod, &code);
-
-        // if (block->rules & Block_Rule_New_Scope)
-        emit_free_local_allocations(mod, &code);
-    }
-
-    if (generate_block_headers) {
-        emit_leave_structured_block(mod, &code);
-        debug_leave_symbol_frame(mod);
-    }
-
-    *pcode = code;
-}
-
-EMIT_FUNC(enter_structured_block, StructuredBlockType sbt, OnyxToken* token) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    static const StructuredBlockType jump_numbers[SBT_Count] = {
-        /* SBT_Basic_Block */       0,
-        /* SBT_Breakable_Block */   1,
-        /* SBT_Continue_Block */    2,
-        /* SBT_Fallthrough_Block */ 3,
-        /* SBT_Return_Block */      4,
-
-        /* SBT_Basic_If */          0,
-        /* SBT_Breakable_If */      1,
-
-        /* SBT_Basic_Loop */        0,
-        /* SBT_Continue_Loop */     2,
-    };
-
-    static const WasmInstructionType block_instrs[SBT_Count] = {
-        /* SBT_Basic_Block */       WI_BLOCK_START,
-        /* SBT_Breakable_Block */   WI_BLOCK_START,
-        /* SBT_Continue_Block */    WI_BLOCK_START,
-        /* SBT_Fallthrough_Block */ WI_BLOCK_START,
-        /* SBT_Return_Block */      WI_BLOCK_START,
-
-        /* SBT_Basic_If */          WI_IF_START,
-        /* SBT_Breakable_If */      WI_IF_START,
-
-        /* SBT_Basic_Loop */        WI_LOOP_START,
-        /* SBT_Continue_Loop */     WI_LOOP_START,
-    };
-
-
-    WID(token, block_instrs[sbt], 0x40);
-    bh_arr_push(mod->structured_jump_target, jump_numbers[sbt]);
-
-    *pcode = code;
-}
-
-EMIT_FUNC_NO_ARGS(leave_structured_block) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    WI(NULL, WI_BLOCK_END);
-    bh_arr_pop(mod->structured_jump_target);
-
-    *pcode = code;
-}
-
-i64 get_structured_jump_label(OnyxWasmModule* mod, JumpType jump_type, u32 jump_count) {
-    // :CLEANUP These numbers should become constants because they are shared with
-    // enter_structured_block's definitions.
-    static const u8 wants[Jump_Type_Count] = { 1, 2, 3, 4 };
-
-    i64 labelidx = 0;
-    u8 wanted = wants[jump_type];
-    b32 success = 0;
-
-    i32 len = bh_arr_length(mod->structured_jump_target) - 1;
-    for (u8* t = &bh_arr_last(mod->structured_jump_target); len >= 0; len--, t--) {
-        if (*t == wanted) jump_count--;
-        if (jump_count == 0) {
-            success = 1;
-            break;
-        }
-
-        labelidx++;
-    }
-
-    return (success == 0) ? -1 : labelidx;
-}
-
-EMIT_FUNC(structured_jump, AstJump* jump) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    i64 labelidx = get_structured_jump_label(mod, jump->jump, jump->count);
-
-    if (bh_arr_length(mod->deferred_stmts) > 0) {
-        i32 i = bh_arr_length(mod->deferred_stmts) - 1;
-        i32 d = bh_arr_length(mod->structured_jump_target) - (labelidx + 1);
-
-        while (i >= 0 && (i32) mod->deferred_stmts[i].depth > d) {
-            emit_deferred_stmt(mod, &code, mod->deferred_stmts[i]);
-            i--;
-        }
-    }
-
-    if (labelidx >= 0) {
-        // NOTE: If the previous instruction was a non conditional jump,
-        // don't emit another jump since it will never be reached.
-        if (bh_arr_last(code).type != WI_JUMP)
-            WID(jump->token, WI_JUMP, labelidx);
-    } else {
-        onyx_report_error(jump->token->pos, Error_Critical, "Invalid structured jump.");
-    }
-
-    *pcode = code;
-}
-
-EMIT_FUNC(statement, AstNode* stmt) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-#ifdef ENABLE_DEBUG_INFO
-    debug_set_position(mod, stmt->token);
-#endif
-
-    switch (stmt->kind) {
-        case Ast_Kind_Return:     emit_return(mod, &code, (AstReturn *) stmt); break;
-        case Ast_Kind_If:         emit_if(mod, &code, (AstIfWhile *) stmt); break;
-        case Ast_Kind_Static_If:  emit_if(mod, &code, (AstIfWhile *) stmt); break;
-        case Ast_Kind_While:      emit_while(mod, &code, (AstIfWhile *) stmt); break;
-        case Ast_Kind_For:        emit_for(mod, &code, (AstFor *) stmt); break;
-        case Ast_Kind_Switch:     emit_switch(mod, &code, (AstSwitch *) stmt); break;
-        case Ast_Kind_Jump:       emit_structured_jump(mod, &code, (AstJump *) stmt); break;
-        case Ast_Kind_Block:      emit_block(mod, &code, (AstBlock *) stmt, 1); break;
-        case Ast_Kind_Defer:      emit_defer(mod, &code, (AstDefer *) stmt); break;
-        case Ast_Kind_Local:      emit_local_allocation(mod, &code, (AstTyped *) stmt); break;
-
-        case Ast_Kind_Directive_Remove: emit_remove_directive(mod, &code, (AstDirectiveRemove *) stmt); break;
-        case Ast_Kind_Directive_Insert: break;
-
-        default:                  emit_expression(mod, &code, (AstTyped *) stmt); break;
-    }
-
-    *pcode = code;
-}
-
-EMIT_FUNC(local_allocation, AstTyped* stmt) {
-    u64 local_idx = local_allocate(mod->local_alloc, stmt);
-    bh_imap_put(&mod->local_map, (u64) stmt, local_idx);
-
-    if (local_is_wasm_local(stmt)) {
-        debug_introduce_symbol(mod, stmt->token, DSL_REGISTER, local_idx, stmt->type);
-    } else {
-        debug_introduce_symbol(mod, stmt->token, DSL_STACK, local_idx, stmt->type);
-    }
-
-    if (stmt->kind == Ast_Kind_Local && !(stmt->flags & Ast_Flag_Decl_Followed_By_Init)) {
-        bh_arr(WasmInstruction) code = *pcode;
-        if (local_is_wasm_local(stmt)) {
-            emit_zero_value(mod, &code, onyx_type_to_wasm_type(stmt->type));
-            WIL(stmt->token, WI_LOCAL_SET, local_idx);
-
-        } else {
-            emit_location(mod, &code, stmt);
-            WID(stmt->token, WI_I32_CONST, 0);
-            WID(stmt->token, WI_I32_CONST, type_size_of(stmt->type));
-            if (context.options->use_post_mvp_features) {
-                WID(stmt->token, WI_MEMORY_FILL, 0x00);
-            } else {
-                emit_intrinsic_memory_fill(mod, &code);
-            }
-        }
-
-        *pcode = code;
-    }
-
-    bh_arr_push(mod->local_allocations, ((AllocatedSpace) {
-        .depth = bh_arr_length(mod->structured_jump_target),
-        .expr  = stmt,
-    }));
-}
-
-EMIT_FUNC_NO_ARGS(free_local_allocations) {
-    if (bh_arr_length(mod->local_allocations) == 0) return;
-
-    u64 depth = bh_arr_length(mod->structured_jump_target);
-    while (bh_arr_length(mod->local_allocations) > 0 && bh_arr_last(mod->local_allocations).depth >= depth) {
-        // CHECK: Not sure this next line is okay to be here...
-        bh_imap_delete(&mod->local_map, (u64) bh_arr_last(mod->local_allocations).expr);
-
-        local_free(mod->local_alloc, bh_arr_last(mod->local_allocations).expr);
-        bh_arr_pop(mod->local_allocations);
-    }
-}
-
-EMIT_FUNC(data_relocation, u32 data_id) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    u32 instr_idx = bh_arr_length(code);
-    WID(NULL, WI_PTR_CONST, 0);
-    assert(mod->current_func_idx >= 0);
-
-    DatumPatchInfo patch;
-    patch.kind = Datum_Patch_Instruction;
-    patch.index = mod->current_func_idx;
-    patch.location = instr_idx;
-    patch.data_id = data_id;
-    patch.offset = 0;
-    bh_arr_push(mod->data_patches, patch);
-
-    *pcode = code;
-}
-
-EMIT_FUNC(assignment, AstBinaryOp* assign) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    if (assign->right->type->kind == Type_Kind_Array) {
-        emit_assignment_of_array(mod, &code, assign->left, assign->right);
-        *pcode = code;
-        return;
-    }
-
-    if (assign->right->type->kind == Type_Kind_Compound) {
-        emit_compound_assignment(mod, &code, assign);
-        *pcode = code;
-        return;
-    }
-
-    AstTyped* lval = assign->left;
-
-    if (lval->kind == Ast_Kind_Local || lval->kind == Ast_Kind_Param) {
-        if (bh_imap_get(&mod->local_map, (u64) lval) & LOCAL_IS_WASM) {
-            emit_expression(mod, &code, assign->right);
-
-            u64 localidx = bh_imap_get(&mod->local_map, (u64) lval);
-
-            if (lval->kind == Ast_Kind_Param && type_is_structlike_strict(lval->type)) {
-                u32 mem_count = type_structlike_mem_count(lval->type);
-                fori (i, 0, mem_count) WIL(assign->token, WI_LOCAL_SET, localidx + i);
-
-            } else {
-                WIL(assign->token, WI_LOCAL_SET, localidx);
-            }
-
-            *pcode = code;
-            return;
-        }
-    }
-
-    if (lval->kind == Ast_Kind_Field_Access) {
-        AstFieldAccess* fa = (AstFieldAccess *) lval;
-        if (fa->expr->kind == Ast_Kind_Param && type_is_structlike_strict(fa->expr->type)) {
-            emit_expression(mod, &code, assign->right);
-
-            u64 localidx = bh_imap_get(&mod->local_map, (u64) fa->expr);
-            WIL(assign->token, WI_LOCAL_SET, localidx + fa->idx);
-
-            *pcode = code;
-            return;
-        }
-    }
-
-    if (lval->kind == Ast_Kind_Global) {
-        emit_expression(mod, &code, assign->right);
-
-        i32 globalidx = (i32) bh_imap_get(&mod->index_map, (u64) lval);
-        WID(assign->token, WI_GLOBAL_SET, globalidx);
-
-        *pcode = code;
-        return;
-    }
-
-    u64 offset = 0;
-    emit_location_return_offset(mod, &code, lval, &offset);
-    emit_expression(mod, &code, assign->right);
-    emit_store_instruction(mod, &code, lval->type, offset);
-
-    *pcode = code;
-}
-
-EMIT_FUNC(assignment_of_array, AstTyped* left, AstTyped* right) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    Type* rtype = right->type;
-    assert(rtype->kind == Type_Kind_Array);
-
-    if (right->kind == Ast_Kind_Array_Literal) {
-        Type* elem_type = rtype;
-        u32 elem_count = 1;
-        while (elem_type->kind == Type_Kind_Array) {
-            elem_count *= elem_type->Array.count;
-            elem_type = elem_type->Array.elem;
-        }
-        u32 elem_size = type_size_of(elem_type);
-
-        u64 lptr_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
-
-        emit_location(mod, &code, left);
-        WIL(left->token, WI_LOCAL_SET, lptr_local);
-
-        AstArrayLiteral* al = (AstArrayLiteral *) right;
-        fori (i, 0, elem_count) {
-            WIL(left->token, WI_LOCAL_GET, lptr_local);
-            emit_expression(mod, &code, al->values[i]);
-            emit_store_instruction(mod, &code, elem_type, i * elem_size);
-        }
-
-        local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
-
-    } else {
-        u64 offset = 0;
-        emit_location_return_offset(mod, &code, left, &offset);
-        emit_expression(mod, &code, right);
-        emit_array_store(mod, &code, rtype, offset);
-    }
-
-    *pcode = code;
-    return;
-}
-
-EMIT_FUNC(compound_assignment, AstBinaryOp* assign) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    emit_expression(mod, &code, assign->right);
-
-    AstCompound* compound_lval = (AstCompound *) assign->left;
-    bh_arr_rev_each(AstTyped *, plval, compound_lval->exprs) {
-        AstTyped *lval = *plval;
-
-        if (type_is_structlike_strict(lval->type)) {
-            emit_struct_lval(mod, &code, lval);
-            continue;
-        }
-
-        if (lval->kind == Ast_Kind_Local || lval->kind == Ast_Kind_Param) {
-            if (bh_imap_get(&mod->local_map, (u64) lval) & LOCAL_IS_WASM) {
-                u64 localidx = bh_imap_get(&mod->local_map, (u64) lval);
-                WIL(assign->token, WI_LOCAL_SET, localidx);
-                continue;
-            }
-        }
-
-        WasmType wt = onyx_type_to_wasm_type(lval->type);
-        u64 expr_tmp = local_raw_allocate(mod->local_alloc, wt);
-        WIL(assign->token, WI_LOCAL_SET, expr_tmp);
-        u64 offset = 0;
-        emit_location_return_offset(mod, &code, lval, &offset);
-        WIL(assign->token, WI_LOCAL_GET, expr_tmp);
-
-        local_raw_free(mod->local_alloc, wt);
-        emit_store_instruction(mod, &code, lval->type, offset);
-    }
-
-    *pcode = code;
-    return;
-}
-
-EMIT_FUNC(store_instruction, Type* type, u32 offset) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    if (type_is_compound(type)) {
-        emit_compound_store(mod, pcode, type, offset, 0);
-        return;
-    }
-
-    if (type->kind == Type_Kind_Struct) {
-        assert(bh_arr_length(type->Struct.linear_members) == 1);
-        type = type->Struct.linear_members[0].type;
-    }
-
-    if (type->kind == Type_Kind_Array) {
-        emit_array_store(mod, pcode, type, offset);
-        return;
-    }
-
-    if (type->kind == Type_Kind_Enum)     type = type->Enum.backing;
-    if (type->kind == Type_Kind_Distinct) type = type->Distinct.base_type;
-    if (type->kind == Type_Kind_Function) type = &basic_types[Basic_Kind_U32];
-
-    u32 alignment = type_get_alignment_log2(type);
-
-    i32 store_size  = type_size_of(type);
-    i32 is_basic    = type->kind == Type_Kind_Basic || type->kind == Type_Kind_Pointer;
-
-    if (is_basic && (type->Basic.flags & Basic_Flag_Pointer)) {
-        WID(NULL, WI_I32_STORE, ((WasmInstructionData) { 2, offset }));
-    } else if (is_basic && ((type->Basic.flags & Basic_Flag_Integer)
-                         || (type->Basic.flags & Basic_Flag_Boolean)
-                         || (type->Basic.flags & Basic_Flag_Type_Index))) {
-        if      (store_size == 1)   WID(NULL, WI_I32_STORE_8,  ((WasmInstructionData) { alignment, offset }));
-        else if (store_size == 2)   WID(NULL, WI_I32_STORE_16, ((WasmInstructionData) { alignment, offset }));
-        else if (store_size == 4)   WID(NULL, WI_I32_STORE,    ((WasmInstructionData) { alignment, offset }));
-        else if (store_size == 8)   WID(NULL, WI_I64_STORE,    ((WasmInstructionData) { alignment, offset }));
-    } else if (is_basic && (type->Basic.flags & Basic_Flag_Float)) {
-        if      (store_size == 4)   WID(NULL, WI_F32_STORE, ((WasmInstructionData) { alignment, offset }));
-        else if (store_size == 8)   WID(NULL, WI_F64_STORE, ((WasmInstructionData) { alignment, offset }));
-    } else if (is_basic && (type->Basic.flags & Basic_Flag_SIMD)) {
-        WID(NULL, WI_V128_STORE, ((WasmInstructionData) { alignment, offset }));
-    } else {
-        onyx_report_error((OnyxFilePos) { 0 }, Error_Critical,
-            "Failed to generate store instruction for type '%s'.",
-            type_get_name(type));
-    }
-
-    *pcode = code;
-}
-
-EMIT_FUNC(load_instruction, Type* type, u32 offset) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    if (type_is_compound(type)) {
-        emit_compound_load(mod, pcode, type, offset);
-        return;
-    }
-
-    if (type->kind == Type_Kind_Struct) {
-        assert(bh_arr_length(type->Struct.linear_members) == 1);
-        type = type->Struct.linear_members[0].type;
-    }
-
-    if (type->kind == Type_Kind_Array) {
-        if (offset != 0) {
-            WID(NULL, WI_PTR_CONST, offset);
-            WI(NULL, WI_PTR_ADD);
-        }
-
-        *pcode = code;
-        return;
-    }
-
-    if (type->kind == Type_Kind_Enum)     type = type->Enum.backing;
-    if (type->kind == Type_Kind_Distinct) type = type->Distinct.base_type;
-    if (type->kind == Type_Kind_Function) type = &basic_types[Basic_Kind_U32];
-
-    i32 load_size   = type_size_of(type);
-    i32 is_basic    = type->kind == Type_Kind_Basic || type->kind == Type_Kind_Pointer;
-
-    WasmInstructionType instr = WI_NOP;
-    i32 alignment = type_get_alignment_log2(type);
-
-    if (is_basic && (type->Basic.flags & Basic_Flag_Pointer)) {
-        instr = WI_I32_LOAD;
-        alignment = 2;
-    }
-    else if (is_basic && ((type->Basic.flags & Basic_Flag_Integer)
-                       || (type->Basic.flags & Basic_Flag_Boolean)
-                       || (type->Basic.flags & Basic_Flag_Type_Index))) {
-        if      (load_size == 1) instr = WI_I32_LOAD_8_S;
-        else if (load_size == 2) instr = WI_I32_LOAD_16_S;
-        else if (load_size == 4) instr = WI_I32_LOAD;
-        else if (load_size == 8) instr = WI_I64_LOAD;
-
-        if (load_size < 4 && (type->Basic.flags & Basic_Flag_Unsigned)) instr += 1;
-    }
-    else if (is_basic && (type->Basic.flags & Basic_Flag_Float)) {
-        if      (load_size == 4) instr = WI_F32_LOAD;
-        else if (load_size == 8) instr = WI_F64_LOAD;
-    }
-    else if (is_basic && (type->Basic.flags & Basic_Flag_SIMD)) {
-        instr = WI_V128_LOAD;
-    }
-
-    WID(NULL, instr, ((WasmInstructionData) { alignment, offset }));
-
-    if (instr == WI_NOP) {
-        onyx_report_error((OnyxFilePos) { 0 }, Error_Critical,
-            "Failed to generate load instruction for type '%s'.",
-            type_get_name(type));
-    }
-
-    *pcode = code;
-}
-
-EMIT_FUNC(if, AstIfWhile* if_node) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    if (if_node->initialization != NULL) {
-        // bh_imap_put(&mod->local_map, (u64) if_node->local, local_allocate(mod->local_alloc, (AstTyped *) if_node->local));
-
-        forll (AstNode, stmt, if_node->initialization, next) {
-            emit_statement(mod, &code, stmt);
-        }
-    }
-
-    if (if_node->kind == Ast_Kind_Static_If) {
-        if (static_if_resolution(if_node)) {
-            if (if_node->true_stmt) emit_block(mod, &code, if_node->true_stmt, 1);
-        } else {
-            if (if_node->false_stmt) emit_block(mod, &code, if_node->false_stmt, 1);
-        }
-
-        *pcode = code;
-        return;
-    }
-
-    emit_expression(mod, &code, if_node->cond);
-
-    emit_enter_structured_block(mod, &code, SBT_Basic_If, if_node->token);
-    if (if_node->true_stmt) emit_block(mod, &code, if_node->true_stmt, 0);
-
-    if (if_node->false_stmt) {
-        WI(if_node->false_stmt->token, WI_ELSE);
-
-        if (if_node->false_stmt->kind == Ast_Kind_If) {
-            emit_if(mod, &code, (AstIfWhile *) if_node->false_stmt);
-        } else {
-            emit_block(mod, &code, if_node->false_stmt, 0);
-        }
-    }
-
-    emit_leave_structured_block(mod, &code);
-
-    *pcode = code;
-}
-
-EMIT_FUNC(while, AstIfWhile* while_node) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    if (while_node->initialization != NULL) {
-        forll (AstNode, stmt, while_node->initialization, next) {
-            emit_statement(mod, &code, stmt);
-        }
-    }
-
-    if (while_node->false_stmt == NULL) {
-        emit_enter_structured_block(mod, &code, SBT_Breakable_Block, while_node->token);
-        emit_enter_structured_block(mod, &code, SBT_Continue_Loop, while_node->token);
-
-        if (!while_node->bottom_test) {
-            emit_expression(mod, &code, while_node->cond);
-            WI(NULL, WI_I32_EQZ);
-            WID(NULL, WI_COND_JUMP, 0x01);
-        }
-
-        emit_block(mod, &code, while_node->true_stmt, 0);
-
-        if (while_node->bottom_test) {
-            emit_expression(mod, &code, while_node->cond);
-            WID(while_node->cond->token, WI_COND_JUMP, 0x00);
-
-        } else {
-            if (bh_arr_last(code).type != WI_JUMP)
-                WID(while_node->cond->token, WI_JUMP, 0x00);
-        }
-
-        emit_leave_structured_block(mod, &code);
-        emit_leave_structured_block(mod, &code);
-
-    } else {
-        emit_expression(mod, &code, while_node->cond);
-
-        emit_enter_structured_block(mod, &code, SBT_Breakable_If, while_node->token);
-        emit_enter_structured_block(mod, &code, SBT_Continue_Loop, while_node->token);
-
-        emit_block(mod, &code, while_node->true_stmt, 0);
-
-        emit_expression(mod, &code, while_node->cond);
-        WID(while_node->cond->token, WI_COND_JUMP, 0x00);
-
-        emit_leave_structured_block(mod, &code);
-        WI(while_node->false_stmt->token, WI_ELSE);
-
-        emit_block(mod, &code, while_node->false_stmt, 0);
-
-        emit_leave_structured_block(mod, &code);
-    }
-
-    *pcode = code;
-}
-
-EMIT_FUNC(for_range, AstFor* for_node, u64 iter_local) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    // NOTE: There are some aspects of the code below that rely on the
-    // low, high, and step members to be i32's. This restriction can be lifted,
-    // but it is important to change the code here.
-    //                                              -brendanfh   2020/09/04
-
-    // NOTE: This might not be a range literal
-    AstStructLiteral *range = (AstStructLiteral *) for_node->iter;
-    u64 offset = 0;
-
-    StructMember low_mem, high_mem, step_mem;
-    type_lookup_member(builtin_range_type_type, "low", &low_mem);
-    type_lookup_member(builtin_range_type_type, "high", &high_mem);
-    type_lookup_member(builtin_range_type_type, "step", &step_mem);
-    u64 low_local  = local_raw_allocate(mod->local_alloc, onyx_type_to_wasm_type(low_mem.type));
-    u64 high_local = local_raw_allocate(mod->local_alloc, onyx_type_to_wasm_type(high_mem.type));
-    u64 step_local = local_raw_allocate(mod->local_alloc, onyx_type_to_wasm_type(step_mem.type));
-
-    WIL(for_node->token, WI_LOCAL_SET, step_local);
-    WIL(for_node->token, WI_LOCAL_SET, high_local);
-    WIL(for_node->token, WI_LOCAL_TEE, low_local);
-    WIL(for_node->token, WI_LOCAL_SET, iter_local);
-
-    emit_enter_structured_block(mod, &code, SBT_Breakable_Block, for_node->token);
-    emit_enter_structured_block(mod, &code, SBT_Basic_Loop, for_node->token);
-    emit_enter_structured_block(mod, &code, SBT_Continue_Block, for_node->token);
-
-    if (range->kind == Ast_Kind_Struct_Literal && (range->args.values[2]->flags & Ast_Flag_Comptime) != 0) {
-        AstNumLit *step_value = (AstNumLit *) range->args.values[2];
-        assert(step_value->kind == Ast_Kind_NumLit);
-
-        if (step_value->value.l >= 0) {
-            WIL(for_node->token, WI_LOCAL_GET, iter_local);
-            WIL(for_node->token, WI_LOCAL_GET, high_local);
-            WI(for_node->token, WI_I32_GE_S);
-            WID(for_node->token, WI_COND_JUMP, 0x02);
-        } else {
-            WIL(for_node->token, WI_LOCAL_GET, iter_local);
-            WIL(for_node->token, WI_LOCAL_GET, high_local);
-            WI(for_node->token, WI_I32_LT_S);
-            WID(for_node->token, WI_COND_JUMP, 0x02);
-        }
-
-    } else {
-        WIL(for_node->token, WI_LOCAL_GET, step_local);
-        WID(for_node->token, WI_I32_CONST, 0);
-        WI(for_node->token, WI_I32_GE_S);
-        WID(for_node->token, WI_IF_START, 0x40);
-            WIL(for_node->token, WI_LOCAL_GET, iter_local);
-            WIL(for_node->token, WI_LOCAL_GET, high_local);
-            WI(for_node->token, WI_I32_GE_S);
-            WID(for_node->token, WI_COND_JUMP, 0x03);
-        WI(for_node->token, WI_ELSE);
-            WIL(for_node->token, WI_LOCAL_GET, iter_local);
-            WIL(for_node->token, WI_LOCAL_GET, high_local);
-            WI(for_node->token, WI_I32_LT_S);
-            WID(for_node->token, WI_COND_JUMP, 0x03);
-        WI(for_node->token, WI_IF_END);
-    }
-
-
-    emit_block(mod, &code, for_node->stmt, 0);
-
-    emit_leave_structured_block(mod, &code);
-
-    WIL(for_node->token, WI_LOCAL_GET, iter_local);
-    WIL(for_node->token, WI_LOCAL_GET, step_local);
-    WI(for_node->token, WI_I32_ADD);
-    WIL(for_node->token, WI_LOCAL_SET, iter_local);
-
-    if (bh_arr_last(code).type != WI_JUMP)
-        WID(for_node->token, WI_JUMP, 0x00);
-
-    emit_leave_structured_block(mod, &code);
-    emit_leave_structured_block(mod, &code);
-
-    local_raw_free(mod->local_alloc, onyx_type_to_wasm_type(low_mem.type));
-    local_raw_free(mod->local_alloc, onyx_type_to_wasm_type(high_mem.type));
-    local_raw_free(mod->local_alloc, onyx_type_to_wasm_type(step_mem.type));
-
-    *pcode = code;
-}
-
-EMIT_FUNC(for_array, AstFor* for_node, u64 iter_local) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    u64 end_ptr_local, ptr_local;
-    end_ptr_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
-
-    if (for_node->by_pointer) {
-        ptr_local = iter_local;
-    } else {
-        ptr_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
-    }
-
-    AstLocal* var = for_node->var;
-    b32 it_is_local = (b32) ((iter_local & LOCAL_IS_WASM) != 0);
-    u64 offset = 0;
-
-    u64 elem_size;
-    if (for_node->by_pointer) elem_size = type_size_of(var->type->Pointer.elem);
-    else                      elem_size = type_size_of(var->type);
-
-    WIL(for_node->token, WI_LOCAL_TEE, ptr_local);
-    WIL(for_node->token, WI_PTR_CONST, for_node->iter->type->Array.count * elem_size);
-    WI(for_node->token, WI_PTR_ADD);
-    WIL(for_node->token, WI_LOCAL_SET, end_ptr_local);
-
-    emit_enter_structured_block(mod, &code, SBT_Breakable_Block, for_node->token);
-    emit_enter_structured_block(mod, &code, SBT_Basic_Loop, for_node->token);
-    emit_enter_structured_block(mod, &code, SBT_Continue_Block, for_node->token);
-
-    WIL(for_node->token, WI_LOCAL_GET, ptr_local);
-    WIL(for_node->token, WI_LOCAL_GET, end_ptr_local);
-    WI(for_node->token, WI_PTR_GE);
-    WID(for_node->token, WI_COND_JUMP, 0x02);
-
-    if (!for_node->by_pointer) {
-        if (!it_is_local) emit_local_location(mod, &code, var, &offset);
-
-        WIL(for_node->token, WI_LOCAL_GET, ptr_local);
-        emit_load_instruction(mod, &code, var->type, 0);
-
-        if (!it_is_local) emit_store_instruction(mod, &code, var->type, offset);
-        else              WIL(for_node->token, WI_LOCAL_SET, iter_local);
-    }
-
-    emit_block(mod, &code, for_node->stmt, 0);
-
-    emit_leave_structured_block(mod, &code);
-
-    WIL(for_node->token, WI_LOCAL_GET, ptr_local);
-    WIL(for_node->token, WI_PTR_CONST, elem_size);
-    WI(for_node->token, WI_PTR_ADD);
-    WIL(for_node->token, WI_LOCAL_SET, ptr_local);
-
-    if (bh_arr_last(code).type != WI_JUMP)
-        WID(for_node->token, WI_JUMP, 0x00);
-
-    emit_leave_structured_block(mod, &code);
-    emit_leave_structured_block(mod, &code);
-
-    local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
-    if (!for_node->by_pointer) local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
-
-    *pcode = code;
-}
-
-EMIT_FUNC(for_slice, AstFor* for_node, u64 iter_local) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    u64 end_ptr_local, ptr_local;
-    end_ptr_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
-
-    if (for_node->by_pointer) {
-        ptr_local = iter_local;
-    } else {
-        ptr_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
-    }
-
-    AstLocal* var = for_node->var;
-    b32 it_is_local = (b32) ((iter_local & LOCAL_IS_WASM) != 0);
-    u64 offset = 0;
-
-    u64 elem_size;
-    if (for_node->by_pointer) elem_size = type_size_of(var->type->Pointer.elem);
-    else                      elem_size = type_size_of(var->type);
-
-    WIL(for_node->token, WI_LOCAL_SET, end_ptr_local);
-    WIL(for_node->token, WI_LOCAL_TEE, ptr_local);
-    WIL(for_node->token, WI_LOCAL_GET, end_ptr_local);
-    if (elem_size != 1) {
-        WID(for_node->token, WI_PTR_CONST, elem_size);
-        WI(for_node->token, WI_PTR_MUL);
-    }
-    WI(for_node->token, WI_PTR_ADD);
-    WIL(for_node->token, WI_LOCAL_SET, end_ptr_local);
-
-    emit_enter_structured_block(mod, &code, SBT_Breakable_Block, for_node->token);
-    emit_enter_structured_block(mod, &code, SBT_Basic_Loop, for_node->token);
-    emit_enter_structured_block(mod, &code, SBT_Continue_Block, for_node->token);
-
-    WIL(for_node->token, WI_LOCAL_GET, ptr_local);
-    WIL(for_node->token, WI_LOCAL_GET, end_ptr_local);
-    WI(for_node->token, WI_PTR_GE);
-    WID(for_node->token, WI_COND_JUMP, 0x02);
-
-    if (!for_node->by_pointer) {
-        if (!it_is_local) emit_local_location(mod, &code, var, &offset);
-
-        WIL(for_node->token, WI_LOCAL_GET, ptr_local);
-        emit_load_instruction(mod, &code, var->type, 0);
-
-        if (!it_is_local) emit_store_instruction(mod, &code, var->type, offset);
-        else              WIL(for_node->token, WI_LOCAL_SET, iter_local);
-    }
-
-    emit_block(mod, &code, for_node->stmt, 0);
-
-    emit_leave_structured_block(mod, &code);
-
-    WIL(for_node->token, WI_LOCAL_GET, ptr_local);
-    WIL(for_node->token, WI_PTR_CONST, elem_size);
-    WI(for_node->token, WI_PTR_ADD);
-    WIL(for_node->token, WI_LOCAL_SET, ptr_local);
-
-    if (bh_arr_last(code).type != WI_JUMP)
-        WID(for_node->token, WI_JUMP, 0x00);
-
-    emit_leave_structured_block(mod, &code);
-    emit_leave_structured_block(mod, &code);
-
-    local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
-    if (!for_node->by_pointer) local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
-
-    *pcode = code;
-}
-
-EMIT_FUNC(for_iterator, AstFor* for_node, u64 iter_local) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    // Allocate temporaries for iterator contents
-    u64 iterator_data_ptr    = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
-    u64 iterator_next_func   = local_raw_allocate(mod->local_alloc, WASM_TYPE_FUNC);
-    u64 iterator_close_func  = local_raw_allocate(mod->local_alloc, WASM_TYPE_FUNC);
-    u64 iterator_remove_func = local_raw_allocate(mod->local_alloc, WASM_TYPE_FUNC);
-    u64 iterator_done_bool   = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
-    WIL(for_node->token, WI_LOCAL_SET, iterator_remove_func);
-    WIL(for_node->token, WI_LOCAL_SET, iterator_close_func);
-    WIL(for_node->token, WI_LOCAL_SET, iterator_next_func);
-    WIL(for_node->token, WI_LOCAL_SET, iterator_data_ptr);
-
-
-    {
-        //
-        // This pushes an entry onto the stack of for loops that have
-        // are iterator that can have a '#remove' directive in them.
-        ForRemoveInfo remove_info;
-        remove_info.iterator_data_ptr = iterator_data_ptr;
-        remove_info.iterator_remove_func = iterator_remove_func;
-
-        TypeWithOffset remove_func_type;
-        type_linear_member_lookup(for_node->iter->type, 3, &remove_func_type);
-        remove_info.remove_func_type_idx = generate_type_idx(mod, remove_func_type.type);
-
-        bh_arr_push(mod->for_remove_info, remove_info);
-    }
-
-    AstLocal* var = for_node->var;
-    b32 it_is_local = (b32) ((iter_local & LOCAL_IS_WASM) != 0);
-    u64 offset = 0;
-
-    // Enter a deferred statement for the auto-close
-    emit_enter_structured_block(mod, &code, SBT_Basic_Block, for_node->token);
-
-    if (!for_node->no_close) {
-        TypeWithOffset close_func_type;
-        type_linear_member_lookup(for_node->iter->type, 2, &close_func_type);
-        i32 close_type_idx = generate_type_idx(mod, close_func_type.type);
-
-        WasmInstruction* close_instructions = bh_alloc_array(global_heap_allocator, WasmInstruction, 8);
-        close_instructions[0] = (WasmInstruction) { WI_LOCAL_GET,     { .l = iterator_close_func } };
-        close_instructions[1] = (WasmInstruction) { WI_I32_CONST,     { .l = mod->null_proc_func_idx } };
-        close_instructions[2] = (WasmInstruction) { WI_I32_NE,        { .l = 0x00 } };
-        close_instructions[3] = (WasmInstruction) { WI_IF_START,      { .l = 0x40 } };
-        close_instructions[4] = (WasmInstruction) { WI_LOCAL_GET,     { .l = iterator_data_ptr } };
-        close_instructions[5] = (WasmInstruction) { WI_LOCAL_GET,     { .l = iterator_close_func } };
-        close_instructions[6] = (WasmInstruction) { WI_CALL_INDIRECT, { .l = close_type_idx } };
-        close_instructions[7] = (WasmInstruction) { WI_IF_END,        { .l = 0x00 } };
-
-        emit_defer_code(mod, &code, close_instructions, 8);
-    }
-
-    emit_enter_structured_block(mod, &code, SBT_Breakable_Block, for_node->token);
-    emit_enter_structured_block(mod, &code, SBT_Continue_Loop, for_node->token);
-
-    if (!it_is_local) emit_local_location(mod, &code, var, &offset);
-
-    {
-        WIL(for_node->token, WI_LOCAL_GET, iterator_data_ptr);
-        WIL(for_node->token, WI_LOCAL_GET, iterator_next_func);
-
-        // CLEANUP: Calling a function is way too f-ing complicated. FACTOR IT!!
-        u64 stack_top_idx = bh_imap_get(&mod->index_map, (u64) &builtin_stack_top);
-
-        TypeWithOffset next_func_type;
-        type_linear_member_lookup(for_node->iter->type, 1, &next_func_type);
-        Type* return_type = next_func_type.type->Function.return_type;
-
-        u32 return_size = type_size_of(return_type);
-        u32 return_align = type_alignment_of(return_type);
-        bh_align(return_size, return_align);
-
-        u64 reserve_size = return_size;
-        bh_align(reserve_size, 16);
-
-        WID(for_node->token, WI_GLOBAL_GET, stack_top_idx);
-        WID(for_node->token, WI_PTR_CONST, reserve_size);
-        WI(for_node->token, WI_PTR_ADD);
-        WID(for_node->token, WI_GLOBAL_SET, stack_top_idx);
-
-        i32 type_idx = generate_type_idx(mod, next_func_type.type);
-        WID(for_node->token, WI_CALL_INDIRECT, ((WasmInstructionData) { type_idx, 0x00 }));
-
-        WID(for_node->token, WI_GLOBAL_GET, stack_top_idx);
-        WID(for_node->token, WI_PTR_CONST, reserve_size);
-        WI(for_node->token, WI_PTR_SUB);
-        WID(for_node->token, WI_GLOBAL_SET, stack_top_idx);
-
-        WID(for_node->token, WI_GLOBAL_GET, stack_top_idx);
-        emit_load_instruction(mod, &code, return_type, reserve_size - return_size);
-    }
-
-    WIL(for_node->token, WI_LOCAL_SET, iterator_done_bool);
-
-    if (!it_is_local) emit_store_instruction(mod, &code, var->type, offset);
-    else              WIL(for_node->token, WI_LOCAL_SET, iter_local);
-
-    WIL(for_node->token, WI_LOCAL_GET, iterator_done_bool);
-    WI(for_node->token, WI_I32_EQZ);
-    WID(for_node->token, WI_COND_JUMP, 0x01);
-
-    emit_block(mod, &code, for_node->stmt, 0);
-    WID(for_node->token, WI_JUMP, 0x00);
-
-    emit_leave_structured_block(mod, &code);
-    emit_leave_structured_block(mod, &code);
-
-    emit_deferred_stmts(mod, &code);
-    emit_leave_structured_block(mod, &code);
-
-    bh_arr_pop(mod->for_remove_info);
-
-    local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
-    local_raw_free(mod->local_alloc, WASM_TYPE_FUNC);
-    local_raw_free(mod->local_alloc, WASM_TYPE_FUNC);
-    local_raw_free(mod->local_alloc, WASM_TYPE_INT32);
-    *pcode = code;
-}
-
-EMIT_FUNC(for, AstFor* for_node) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    AstLocal* var = for_node->var;
-    u64 iter_local = local_allocate(mod->local_alloc, (AstTyped *) var);
-    bh_imap_put(&mod->local_map, (u64) var, iter_local);
-
-    debug_enter_symbol_frame(mod);
-    debug_introduce_symbol(mod, var->token,
-        local_is_wasm_local((AstTyped *) var) ? DSL_REGISTER : DSL_STACK,
-        iter_local, var->type);
-
-    emit_expression(mod, &code, for_node->iter);
-
-    switch (for_node->loop_type) {
-        case For_Loop_Range:    emit_for_range(mod, &code, for_node, iter_local); break;
-        case For_Loop_Array:    emit_for_array(mod, &code, for_node, iter_local); break;
-        // NOTE: A dynamic array is just a slice with a capacity and allocator on the end.
-        // Just dropping the extra fields will mean we can just use the slice implementation.
-        //                                                  - brendanfh   2020/09/04
-        //                                                  - brendanfh   2021/04/13
-        case For_Loop_DynArr:   WI(for_node->token, WI_DROP); WI(for_node->token, WI_DROP); WI(for_node->token, WI_DROP);
-        case For_Loop_Slice:    emit_for_slice(mod, &code, for_node, iter_local); break;
-        case For_Loop_Iterator: emit_for_iterator(mod, &code, for_node, iter_local); break;
-        default: onyx_report_error(for_node->token->pos, Error_Critical, "Invalid for loop type. You should probably not be seeing this...");
-    }
-
-    local_free(mod->local_alloc, (AstTyped *) var);
-    debug_leave_symbol_frame(mod);
-
-    *pcode = code;
-}
-
-EMIT_FUNC(switch, AstSwitch* switch_node) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    bh_imap block_map;
-    bh_imap_init(&block_map, global_heap_allocator, bh_arr_length(switch_node->cases));
-
-    if (switch_node->initialization != NULL) {
-        forll (AstNode, stmt, switch_node->initialization, next) {
-            emit_statement(mod, &code, stmt);
-        }
-    }
-
-    emit_enter_structured_block(mod, &code, SBT_Breakable_Block, switch_node->token);
-
-    u64 block_num = 0;
-    bh_arr_each(AstSwitchCase *, sc, switch_node->cases) {
-        if (bh_imap_has(&block_map, (u64) (*sc)->block)) continue;
-
-        emit_enter_structured_block(mod, &code, SBT_Fallthrough_Block, (*sc)->block->token);
-
-        bh_imap_put(&block_map, (u64) (*sc)->block, block_num);
-        block_num++;
-    }
-
-    switch (switch_node->switch_kind) {
-        case Switch_Kind_Integer: {
-            u64 count = switch_node->max_case + 1 - switch_node->min_case;
-            BranchTable* bt = bh_alloc(mod->extended_instr_alloc, sizeof(BranchTable) + sizeof(u32) * count);
-            bt->count = count;
-            bt->default_case = block_num;
-            fori (i, 0, bt->count) bt->cases[i] = bt->default_case;
-
-            bh_arr_each(bh__imap_entry, sc, switch_node->case_map.entries) {
-                bt->cases[sc->key - switch_node->min_case] = bh_imap_get(&block_map, (u64) sc->value);
-            }
-
-            // NOTE: We enter a new block here in order to setup the correct
-            // indicies for the jump targets in the branch table. For example,
-            //
-            // <expr>
-            // jump_table
-            // label0:
-            // ...
-            // label1:
-            // ...
-            //
-            // If we didn't enter a new block, then jumping to label 0, would jump
-            // to the second block, and so on.
-            WID(switch_node->expr->token, WI_BLOCK_START, 0x40);
-            emit_expression(mod, &code, switch_node->expr);
-            if (switch_node->min_case != 0) {
-                WID(switch_node->expr->token, WI_I32_CONST, switch_node->min_case);
-                WI(switch_node->expr->token, WI_I32_SUB);
-            }
-            WIP(switch_node->expr->token, WI_JUMP_TABLE, bt);
-            WI(switch_node->expr->token, WI_BLOCK_END);
-            break;
-        }
-
-        case Switch_Kind_Use_Equals: {
-            WID(switch_node->expr->token, WI_BLOCK_START, 0x40);
-
-            bh_arr_each(CaseToBlock, ctb, switch_node->case_exprs) {
-                emit_expression(mod, &code, (AstTyped *) ctb->comparison);
-
-                u64 bn = bh_imap_get(&block_map, (u64) ctb->block);
-                WID(switch_node->expr->token, WI_IF_START, 0x40);
-                WID(switch_node->expr->token, WI_JUMP, bn + 1);
-                WI(switch_node->expr->token, WI_IF_END);
-            }
-
-            WID(switch_node->expr->token, WI_JUMP, block_num);
-            WI(switch_node->expr->token, WI_BLOCK_END);
-            break;
-        }
-    }
-
-    bh_arr_each(AstSwitchCase *, psc, switch_node->cases) {
-        AstSwitchCase *sc = *psc;
-        if (bh_imap_get(&block_map, (u64) sc->block) == 0xdeadbeef) continue;
-
-        u64 bn = bh_imap_get(&block_map, (u64) sc->block);
-
-        // Maybe the Symbol Frame idea should be controlled as a block_flag?
-        debug_enter_symbol_frame(mod);
-        emit_block(mod, &code, sc->block, 0);
-        debug_leave_symbol_frame(mod);
-
-        if (bh_arr_last(code).type != WI_JUMP)
-            WID(NULL, WI_JUMP, block_num - bn);
-
-        emit_leave_structured_block(mod, &code);
-
-        bh_imap_put(&block_map, (u64) sc->block, 0xdeadbeef);
-    }
-
-    if (switch_node->default_case != NULL) {
-        emit_block(mod, &code, switch_node->default_case, 0);
-    }
-
-    emit_leave_structured_block(mod, &code);
-
-    bh_imap_free(&block_map);
-    *pcode = code;
-}
-
-EMIT_FUNC(defer, AstDefer* defer) {
-    bh_arr_push(mod->deferred_stmts, ((DeferredStmt) {
-        .type = Deferred_Stmt_Node,
-        .depth = bh_arr_length(mod->structured_jump_target),
-        .defer_node= defer,
-        .stmt = defer->stmt,
-    }));
-}
-
-EMIT_FUNC(defer_code, WasmInstruction* deferred_code, u32 code_count) {
-    bh_arr_push(mod->deferred_stmts, ((DeferredStmt) {
-        .type = Deferred_Stmt_Code,
-        .depth = bh_arr_length(mod->structured_jump_target),
-        .defer_node= NULL,
-        .instructions = deferred_code,
-        .instruction_count = code_count,
-    }));
-}
-
-EMIT_FUNC(deferred_stmt, DeferredStmt deferred_stmt) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    if (deferred_stmt.defer_node) {
-        WI(deferred_stmt.defer_node->token, WI_NOP);
-    }
-
-    switch (deferred_stmt.type) {
-        case Deferred_Stmt_Node: emit_statement(mod, &code, deferred_stmt.stmt); break;
-        case Deferred_Stmt_Code: {
-            fori (i, 0, deferred_stmt.instruction_count) {
-                WIR(NULL, deferred_stmt.instructions[i]);
-            }
-            break;
-        }
-    }
-
-    *pcode = code;
-}
-
-EMIT_FUNC_NO_ARGS(deferred_stmts) {
-    if (bh_arr_length(mod->deferred_stmts) <= 0) return;
-
-    u64 depth = bh_arr_length(mod->structured_jump_target);
-
-    while (bh_arr_length(mod->deferred_stmts) > 0 && bh_arr_last(mod->deferred_stmts).depth >= depth) {
-        DeferredStmt stmt = bh_arr_pop(mod->deferred_stmts);
-        emit_deferred_stmt(mod, pcode, stmt);
-    }
-}
-
-EMIT_FUNC(remove_directive, AstDirectiveRemove* remove) {
-    assert(bh_arr_length(mod->for_remove_info) > 0);
-
-    bh_arr(WasmInstruction) code = *pcode;
-
-    ForRemoveInfo remove_info = bh_arr_last(mod->for_remove_info);
-
-    WIL(remove->token, WI_LOCAL_GET, remove_info.iterator_remove_func);
-    WIL(remove->token, WI_I32_CONST, mod->null_proc_func_idx);
-    WI(remove->token, WI_I32_NE);
-    WID(remove->token, WI_IF_START, 0x40);
-    WIL(remove->token, WI_LOCAL_GET, remove_info.iterator_data_ptr);
-    WIL(remove->token, WI_LOCAL_GET, remove_info.iterator_remove_func);
-    WIL(remove->token, WI_CALL_INDIRECT, remove_info.remove_func_type_idx);
-    WI(remove->token, WI_IF_END);
-
-    *pcode = code;
-}
-
-// NOTE: These need to be in the same order as
-// the OnyxBinaryOp enum
-static const WasmInstructionType binop_map[][4] = {
-    //           I32           I64           F32         F64
-    /* ADD */  { WI_I32_ADD,   WI_I64_ADD,   WI_F32_ADD, WI_F64_ADD },
-    /* SUB */  { WI_I32_SUB,   WI_I64_SUB,   WI_F32_SUB, WI_F64_SUB },
-    /* MUL */  { WI_I32_MUL,   WI_I64_MUL,   WI_F32_MUL, WI_F64_MUL },
-    /* DIV */  { WI_I32_DIV_S, WI_I64_DIV_S, WI_F32_DIV, WI_F64_DIV },
-    /* REM */  { WI_I32_REM_S, WI_I64_REM_S, WI_NOP,     WI_NOP     },
-
-    /* EQ  */  { WI_I32_EQ,    WI_I64_EQ,    WI_F32_EQ,  WI_F64_EQ },
-    /* NEQ */  { WI_I32_NE,    WI_I64_NE,    WI_F32_NE , WI_F64_NE },
-    /* LT  */  { WI_I32_LT_S,  WI_I64_LT_S,  WI_F32_LT,  WI_F64_LT },
-    /* LTE */  { WI_I32_LE_S,  WI_I64_LE_S,  WI_F32_LE,  WI_F64_LE },
-    /* GT  */  { WI_I32_GT_S,  WI_I64_GT_S,  WI_F32_GT,  WI_F64_GT },
-    /* GTE */  { WI_I32_GE_S,  WI_I64_GE_S,  WI_F32_GE,  WI_F64_GE },
-
-    /* AND */  { WI_I32_AND,   WI_I64_AND,   WI_NOP,     WI_NOP },
-    /* OR  */  { WI_I32_OR,    WI_I64_OR,    WI_NOP,     WI_NOP },
-    /* XOR */  { WI_I32_XOR,   WI_I64_XOR,   WI_NOP,     WI_NOP },
-    /* SHL */  { WI_I32_SHL,   WI_I64_SHL,   WI_NOP,     WI_NOP },
-    /* SHR */  { WI_I32_SHR_U, WI_I64_SHR_U, WI_NOP,     WI_NOP },
-    /* SAR */  { WI_I32_SHR_S, WI_I64_SHR_S, WI_NOP,     WI_NOP },
-
-    /* BAND */ { WI_I32_AND,   WI_I64_AND,   WI_NOP,     WI_NOP },
-    /* BOR  */ { WI_I32_OR,    WI_I64_OR,    WI_NOP,     WI_NOP },
-};
-
-EMIT_FUNC(binop, AstBinaryOp* binop) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    if (binop_is_assignment(binop->operation)) {
-        emit_assignment(mod, &code, binop);
-        *pcode = code;
-        return;
-    }
-
-    b32 is_sign_significant = 0;
-    switch (binop->operation) {
-        case Binary_Op_Divide:  case Binary_Op_Modulus:
-        case Binary_Op_Less:    case Binary_Op_Less_Equal:
-        case Binary_Op_Greater: case Binary_Op_Greater_Equal:
-            is_sign_significant = 1;
-    }
-
-    WasmType operator_type = onyx_type_to_wasm_type(binop->left->type);
-    i32 optype = 0;
-    if      (operator_type == WASM_TYPE_INT32)   optype = 0;
-    else if (operator_type == WASM_TYPE_INT64)   optype = 1;
-    else if (operator_type == WASM_TYPE_FLOAT32) optype = 2;
-    else if (operator_type == WASM_TYPE_FLOAT64) optype = 3;
-
-    WasmInstructionType binop_instr = binop_map[(i32) binop->operation][optype];
-
-    assert(binop_instr != WI_NOP);
-
-    // NOTE: Use unsigned variant if needed
-    // Unsigned instructions are always right after
-    // the signed equivalent
-    if (is_sign_significant) {
-        if (binop->left->type->Basic.flags & Basic_Flag_Unsigned) {
-            binop_instr = (WasmInstructionType) ((i32) binop_instr + 1);
-        }
-    }
-
-    emit_expression(mod, &code, binop->left);
-    emit_expression(mod, &code, binop->right);
-
-    WI(binop->token, binop_instr);
-
-    *pcode = code;
-}
-
-EMIT_FUNC(unaryop, AstUnaryOp* unop) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    switch (unop->operation) {
-        case Unary_Op_Negate: {
-            TypeBasic* type = &unop->type->Basic;
-
-            if (type->kind == Basic_Kind_I32
-                    || type->kind == Basic_Kind_I16
-                    || type->kind == Basic_Kind_I8) {
-                WID(unop->token, WI_I32_CONST, 0x00);
-                emit_expression(mod, &code, unop->expr);
-                WI(unop->token, WI_I32_SUB);
-
-            }
-            else if (type->kind == Basic_Kind_I64) {
-                WID(unop->token, WI_I64_CONST, 0x00);
-                emit_expression(mod, &code, unop->expr);
-                WI(unop->token, WI_I64_SUB);
-
-            }
-            else {
-                emit_expression(mod, &code, unop->expr);
-
-                if (type->kind == Basic_Kind_F32) WI(unop->token, WI_F32_NEG);
-                if (type->kind == Basic_Kind_F64) WI(unop->token, WI_F64_NEG);
-            }
-
-            break;
-        }
-
-        case Unary_Op_Not:
-            emit_expression(mod, &code, unop->expr);
-
-            WI(unop->token, WI_I32_EQZ);
-            break;
-
-        case Unary_Op_Bitwise_Not: {
-            emit_expression(mod, &code, unop->expr);
-
-            TypeBasic* type = &unop->type->Basic;
-
-            if (type->kind == Basic_Kind_I8 || type->kind == Basic_Kind_U8) {
-                WID(unop->token, WI_I32_CONST, 0xff);
-                WI(unop->token, WI_I32_XOR);
-            }
-            else if (type->kind == Basic_Kind_I16 || type->kind == Basic_Kind_U16) {
-                WID(unop->token, WI_I32_CONST, 0xffff);
-                WI(unop->token, WI_I32_XOR);
-            }
-            else if (type->kind == Basic_Kind_I32 || type->kind == Basic_Kind_U32) {
-                WID(unop->token, WI_I32_CONST, 0xffffffff);
-                WI(unop->token, WI_I32_XOR);
-            }
-            else if (type->kind == Basic_Kind_I64 || type->kind == Basic_Kind_U64) {
-                WIL(unop->token, WI_I64_CONST, 0xffffffffffffffff);
-                WI(unop->token, WI_I64_XOR);
-            }
-
-            break;
-        }
-
-        case Unary_Op_Auto_Cast:
-        case Unary_Op_Cast: emit_cast(mod, &code, unop); break;
-    }
-
-    *pcode = code;
-}
-
-// Calling a procedure in Onyx.
-//
-// This documentation should be placed elsewhere, but for right now I'm going to write it in the relevant
-// piece of code. Calling a procedure is relatively simple, at least compared to other calling conventions
-// out there, mostly due the fact that this is WebAssembly, where registers are "infinite" and there's no
-// really need to use stack canaries for security.
-//
-// The biggest piece to understand is how the stack gets laid out for the called procedure. To be confusing,
-// there are two stacks at play: the WASM expression stack, and the linear memory stack. Here is the general
-// lay out for calling a procedure with the following signature.
-//
-//    foo :: (x: i32, y: str, z: [..] i32, va: ..i32) -> (i32, i32)
-//
-// WASM stack: top is last pushed
-//
-//    vararg count
-//    vararg pointer to variadic arguments in the linear memory
-//    pointer to struct-like arguments (z)
-//    simple structs/primitives (y, x)
-//
-// Linear memory stack:
-//
-//     ... | struct-like arguments (z) | variadic arguments (va) | return space | ...
-//
-// The interesting part from above is the fact that 'y' gets passed on the WASM stack, not the linear memory
-// stack, even though a 'str' in Onyx is 2-component structure. This is because so-called "simple" structures,
-// i.e. structures that are completely flat, with no sub-structures, are passed as multiple primitives. I do
-// this because many times for interoperability, it is nicer to get two primitive values for the pointer and
-// count of a slice, instead of a pointer.
-EMIT_FUNC(call, AstCall* call) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    u64 stack_top_idx = bh_imap_get(&mod->index_map, (u64) &builtin_stack_top);
-    u64 stack_top_store_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
-
-    OnyxToken* call_token = call->token;
-
-    // Because it would be inefficient to increment and decrement the global stack pointer for every argument,
-    // a simple set of instructions increments it once to the size it will need to be. However, because it is
-    // impossible to know what size the reserved memory will be, a location patch is taken in order to fill it
-    // in later.
-    u32 reserve_space_patch = bh_arr_length(code);
-    WID(call_token, WI_GLOBAL_GET, stack_top_idx);
-    WIL(call_token, WI_LOCAL_TEE, stack_top_store_local);
-    WID(call_token, WI_PTR_CONST, 0);                           // This will be filled in later.
-    WI(call_token, WI_PTR_ADD);
-    WID(call_token, WI_GLOBAL_SET, stack_top_idx);
-
-    u32 reserve_size  = 0;
-    u32 vararg_count  = 0;
-    i32 vararg_offset = -1;
-
-    u32* vararg_any_offsets=NULL;
-    u32* vararg_any_types=NULL;
-    if (call->va_kind == VA_Kind_Any) {
-        vararg_any_offsets = bh_alloc_array(global_scratch_allocator, u32, bh_arr_length(call->args.values));
-        vararg_any_types   = bh_alloc_array(global_scratch_allocator, u32, bh_arr_length(call->args.values));
-    }
-
-    bh_arr_each(AstTyped *, parg, call->args.values) {
-        AstArgument* arg = (AstArgument *) *parg;
-        if (arg->is_baked) continue;
-
-        b32 place_on_stack = 0;
-
-        if (type_get_param_pass(arg->value->type) == Param_Pass_By_Implicit_Pointer) {
-            // This arguments needs to be written to the stack because it is not a simple structure.
-            place_on_stack = 1;
-        }
-
-        if (arg->va_kind != VA_Kind_Not_VA) {
-            // This is a variadic argument and needs to be written to the stack. If the starting
-            // location of the vararg array hasn't been noted, note it.
-            if (vararg_offset < 0) vararg_offset = reserve_size;
-
-            place_on_stack = 1;
-            vararg_count += 1;
-        }
-
-        if (arg->pass_as_any) {
-            place_on_stack = 1;
-        }
-
-        if (place_on_stack) WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
-
-        emit_expression(mod, &code, arg->value);
-
-        if (place_on_stack) {
-            emit_store_instruction(mod, &code, arg->value->type, reserve_size);
-
-            if (arg->va_kind == VA_Kind_Not_VA) {
-                // Non-variadic arguments on the stack need a pointer to them placed on the WASM stack.
-                WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
-                WID(call_token, WI_PTR_CONST, reserve_size);
-                WI(call_token, WI_PTR_ADD);
-            }
-
-            if (arg->va_kind == VA_Kind_Any) {
-                vararg_any_offsets[vararg_count - 1] = reserve_size;
-                vararg_any_types[vararg_count - 1] = arg->value->type->id;
-            }
-
-            if (arg->pass_as_any) {
-                WIL(call_token, WI_I32_CONST, arg->value->type->id);
-            }
-
-            reserve_size += type_size_of(arg->value->type);
-        }
-    }
-
-    switch (call->va_kind) {
-        case VA_Kind_Any: {
-            vararg_offset = reserve_size;
-
-            i32 any_size = type_size_of(type_build_from_ast(context.ast_alloc, builtin_any_type));
-
-            fori (i, 0, vararg_count) {
-                WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
-                WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
-                WID(call_token, WI_PTR_CONST, vararg_any_offsets[i]);
-                WI(call_token, WI_PTR_ADD);
-                emit_store_instruction(mod, &code, &basic_types[Basic_Kind_Rawptr], vararg_offset + i * any_size);
-
-                WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
-                WID(call_token, WI_I32_CONST, vararg_any_types[i]);
-                emit_store_instruction(mod, &code, &basic_types[Basic_Kind_Type_Index], vararg_offset + i * any_size + POINTER_SIZE);
-
-                reserve_size += any_size;
-            }
-
-            // fallthrough
-        }
-
-        case VA_Kind_Typed: {
-            WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
-            if (vararg_offset > 0) {
-                WID(call_token, WI_PTR_CONST, vararg_offset);
-                WI(call_token, WI_PTR_ADD);
-            }
-            WID(call_token, WI_I32_CONST, vararg_count);
-            break;
-        }
-
-        case VA_Kind_Untyped: {
-            WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
-            WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
-            if (vararg_offset > 0) {
-                WID(call_token, WI_PTR_CONST, vararg_offset);
-                WI(call_token, WI_PTR_ADD);
-            }
-            emit_store_instruction(mod, &code, &basic_types[Basic_Kind_Rawptr], reserve_size);
-
-            // NOTE: There may be 4 uninitialized bytes here, because pointers are only 4 bytes in WASM.
-
-            WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
-            WID(call_token, WI_I32_CONST, vararg_count);
-            emit_store_instruction(mod, &code, &basic_types[Basic_Kind_I32], reserve_size + POINTER_SIZE);
-
-            WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
-            if (reserve_size > 0) {
-                WID(call_token, WI_PTR_CONST, reserve_size);
-                WI(call_token, WI_PTR_ADD);
-            }
-
-            reserve_size += 4 + POINTER_SIZE;
-            break;
-        }
-    }
-
-    CallingConvention cc = type_function_get_cc(call->callee->type);
-    assert(cc != CC_Undefined);
-
-    Type* return_type = call->callee->type->Function.return_type;
-    u32 return_size = type_size_of(return_type);
-    assert(return_size % type_alignment_of(return_type) == 0);
-
-    if (cc == CC_Return_Stack) reserve_size += return_size;
-
-    if (call->callee->kind == Ast_Kind_Function) {
-        i32 func_idx = (i32) bh_imap_get(&mod->index_map, (u64) call->callee);
-        WIL(NULL, WI_CALL, func_idx);
-
-    } else {
-        emit_expression(mod, &code, call->callee);
-
-        i32 type_idx = generate_type_idx(mod, call->callee->type);
-        WID(NULL, WI_CALL_INDIRECT, ((WasmInstructionData) { type_idx, 0x00 }));
-    }
-
-    if (reserve_size > 0) {
-        WIL(call_token, WI_LOCAL_GET,  stack_top_store_local);
-        WID(call_token, WI_GLOBAL_SET, stack_top_idx);
-
-        bh_align(reserve_size, 16);
-        code[reserve_space_patch + 2].data.l = reserve_size;
-
-    } else {
-        fori (i, 0, 5) code[reserve_space_patch + i].type = WI_NOP;
-    }
-
-    if (cc == CC_Return_Stack) {
-        WID(call_token, WI_GLOBAL_GET, stack_top_idx);
-        emit_load_instruction(mod, &code, return_type, reserve_size - return_size);
-    }
-
-    local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
-    *pcode = code;
-}
-
-// BUG: This implementation assumes that the host system C's implementation is using
-// little endian integers.
-#define SIMD_INT_CONST_INTRINSIC(type, count) { \
-        type* byte_buffer = bh_alloc(mod->extended_instr_alloc, 16); \
-        bh_arr(AstArgument *) arg_arr = (bh_arr(AstArgument *)) call->args.values; \
-        fori (i, 0, count) { \
-            if (arg_arr[i]->value->kind != Ast_Kind_NumLit) { \
-                onyx_report_error(arg_arr[i]->token->pos, Error_Critical, \
-                        "SIMD constants expect compile time constants as parameters. The %d%s parameter was not.", \
-                        i, bh_num_suffix(i)); \
-                *pcode = code; \
-                return; \
-            } \
-            byte_buffer[i] = (type) ((AstNumLit *) arg_arr[i]->value)->value.l; \
-        } \
-        WIP(call->token, WI_V128_CONST, byte_buffer); \
-    }
-
-#define SIMD_EXTRACT_LANE_INSTR(instr, arg_arr) \
-    emit_expression(mod, &code, arg_arr[0]->value);\
-    if (arg_arr[1]->value->kind != Ast_Kind_NumLit) { \
-        onyx_report_error(arg_arr[1]->token->pos, Error_Critical, "SIMD lane instructions expect a compile time lane number."); \
-        *pcode = code; \
-        return; \
-    } \
-    WID(call->token, instr, (u8) ((AstNumLit *) arg_arr[1]->value)->value.i);
-
-#define SIMD_REPLACE_LANE_INSTR(instr, arg_arr) { \
-    emit_expression(mod, &code, arg_arr[0]->value);\
-    if (arg_arr[1]->value->kind != Ast_Kind_NumLit) { \
-        onyx_report_error(arg_arr[1]->token->pos, Error_Critical, "SIMD lane instructions expect a compile time lane number."); \
-        *pcode = code; \
-        return; \
-    } \
-    u8 lane = (u8) ((AstNumLit *) arg_arr[1]->value)->value.i; \
-    emit_expression(mod, &code, arg_arr[2]->value); \
-    WID(call->token, instr, lane); \
-}
-
-
-EMIT_FUNC(intrinsic_call, AstCall* call) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    b32 place_arguments_normally = 1;
-
-    switch (call->intrinsic) {
-        case ONYX_INTRINSIC_V128_CONST:
-        case ONYX_INTRINSIC_I8X16_CONST: case ONYX_INTRINSIC_I16X8_CONST:
-        case ONYX_INTRINSIC_I32X4_CONST: case ONYX_INTRINSIC_I64X2_CONST:
-        case ONYX_INTRINSIC_F32X4_CONST: case ONYX_INTRINSIC_F64X2_CONST:
-        case ONYX_INTRINSIC_I8X16_EXTRACT_LANE_S: case ONYX_INTRINSIC_I8X16_EXTRACT_LANE_U:
-        case ONYX_INTRINSIC_I16X8_EXTRACT_LANE_S: case ONYX_INTRINSIC_I16X8_EXTRACT_LANE_U:
-        case ONYX_INTRINSIC_I32X4_EXTRACT_LANE:   case ONYX_INTRINSIC_I64X2_EXTRACT_LANE:
-        case ONYX_INTRINSIC_F32X4_EXTRACT_LANE:   case ONYX_INTRINSIC_F64X2_EXTRACT_LANE:
-        case ONYX_INTRINSIC_I8X16_REPLACE_LANE:   case ONYX_INTRINSIC_I16X8_REPLACE_LANE:
-        case ONYX_INTRINSIC_I32X4_REPLACE_LANE:   case ONYX_INTRINSIC_I64X2_REPLACE_LANE:
-        case ONYX_INTRINSIC_F32X4_REPLACE_LANE:   case ONYX_INTRINSIC_F64X2_REPLACE_LANE:
-        case ONYX_INTRINSIC_I8X16_SHUFFLE:
-            place_arguments_normally = 0;
-
-        default: break;
-    }
-
-    if (place_arguments_normally) {
-        bh_arr_each(AstTyped *, arg, call->args.values) {
-            emit_expression(mod, &code, *arg);
-        }
-    }
-
-    switch (call->intrinsic) {
-        case ONYX_INTRINSIC_MEMORY_SIZE:  WID(call->token, WI_MEMORY_SIZE, 0x00); break;
-        case ONYX_INTRINSIC_MEMORY_GROW:  WID(call->token, WI_MEMORY_GROW, 0x00); break;
-        case ONYX_INTRINSIC_MEMORY_COPY:
-            if (context.options->use_post_mvp_features) {
-                WIL(call->token, WI_MEMORY_COPY, 0x00);
-            } else {
-                emit_intrinsic_memory_copy(mod, &code);
-            }
-            break;
-        case ONYX_INTRINSIC_MEMORY_FILL:
-            if (context.options->use_post_mvp_features) {
-                WIL(call->token, WI_MEMORY_FILL, 0x00);
-            } else {
-                emit_intrinsic_memory_fill(mod, &code);
-            }
-            break;
-
-        case ONYX_INTRINSIC_INITIALIZE: {
-            Type* type_to_initialize = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
-            emit_initialize_type(mod, &code, type_to_initialize, call->token);
-            break;
-        }
-
-        case ONYX_INTRINSIC_I32_CLZ:      WI(call->token, WI_I32_CLZ); break;
-        case ONYX_INTRINSIC_I32_CTZ:      WI(call->token, WI_I32_CTZ); break;
-        case ONYX_INTRINSIC_I32_POPCNT:   WI(call->token, WI_I32_POPCNT); break;
-        case ONYX_INTRINSIC_I32_AND:      WI(call->token, WI_I32_AND); break;
-        case ONYX_INTRINSIC_I32_OR:       WI(call->token, WI_I32_OR); break;
-        case ONYX_INTRINSIC_I32_XOR:      WI(call->token, WI_I32_XOR); break;
-        case ONYX_INTRINSIC_I32_SHL:      WI(call->token, WI_I32_SHL); break;
-        case ONYX_INTRINSIC_I32_SLR:      WI(call->token, WI_I32_SHR_U); break;
-        case ONYX_INTRINSIC_I32_SAR:      WI(call->token, WI_I32_SHR_S); break;
-        case ONYX_INTRINSIC_I32_ROTL:     WI(call->token, WI_I32_ROTL); break;
-        case ONYX_INTRINSIC_I32_ROTR:     WI(call->token, WI_I32_ROTR); break;
-
-        case ONYX_INTRINSIC_I64_CLZ:      WI(call->token, WI_I64_CLZ); break;
-        case ONYX_INTRINSIC_I64_CTZ:      WI(call->token, WI_I64_CTZ); break;
-        case ONYX_INTRINSIC_I64_POPCNT:   WI(call->token, WI_I64_POPCNT); break;
-        case ONYX_INTRINSIC_I64_AND:      WI(call->token, WI_I64_AND); break;
-        case ONYX_INTRINSIC_I64_OR:       WI(call->token, WI_I64_OR); break;
-        case ONYX_INTRINSIC_I64_XOR:      WI(call->token, WI_I64_XOR); break;
-        case ONYX_INTRINSIC_I64_SHL:      WI(call->token, WI_I64_SHL); break;
-        case ONYX_INTRINSIC_I64_SLR:      WI(call->token, WI_I64_SHR_U); break;
-        case ONYX_INTRINSIC_I64_SAR:      WI(call->token, WI_I64_SHR_S); break;
-        case ONYX_INTRINSIC_I64_ROTL:     WI(call->token, WI_I64_ROTL); break;
-        case ONYX_INTRINSIC_I64_ROTR:     WI(call->token, WI_I64_ROTR); break;
-
-        case ONYX_INTRINSIC_F32_ABS:      WI(call->token, WI_F32_ABS); break;
-        case ONYX_INTRINSIC_F32_CEIL:     WI(call->token, WI_F32_CEIL); break;
-        case ONYX_INTRINSIC_F32_FLOOR:    WI(call->token, WI_F32_FLOOR); break;
-        case ONYX_INTRINSIC_F32_TRUNC:    WI(call->token, WI_F32_TRUNC); break;
-        case ONYX_INTRINSIC_F32_NEAREST:  WI(call->token, WI_F32_NEAREST); break;
-        case ONYX_INTRINSIC_F32_SQRT:     WI(call->token, WI_F32_SQRT); break;
-        case ONYX_INTRINSIC_F32_MIN:      WI(call->token, WI_F32_MIN); break;
-        case ONYX_INTRINSIC_F32_MAX:      WI(call->token, WI_F32_MAX); break;
-        case ONYX_INTRINSIC_F32_COPYSIGN: WI(call->token, WI_F32_COPYSIGN); break;
-
-        case ONYX_INTRINSIC_F64_ABS:      WI(call->token, WI_F64_ABS); break;
-        case ONYX_INTRINSIC_F64_CEIL:     WI(call->token, WI_F64_CEIL); break;
-        case ONYX_INTRINSIC_F64_FLOOR:    WI(call->token, WI_F64_FLOOR); break;
-        case ONYX_INTRINSIC_F64_TRUNC:    WI(call->token, WI_F64_TRUNC); break;
-        case ONYX_INTRINSIC_F64_NEAREST:  WI(call->token, WI_F64_NEAREST); break;
-        case ONYX_INTRINSIC_F64_SQRT:     WI(call->token, WI_F64_SQRT); break;
-        case ONYX_INTRINSIC_F64_MIN:      WI(call->token, WI_F64_MIN); break;
-        case ONYX_INTRINSIC_F64_MAX:      WI(call->token, WI_F64_MAX); break;
-        case ONYX_INTRINSIC_F64_COPYSIGN: WI(call->token, WI_F64_COPYSIGN); break;
-
-        case ONYX_INTRINSIC_I8X16_CONST:
-        case ONYX_INTRINSIC_V128_CONST:   SIMD_INT_CONST_INTRINSIC(u8, 16);   break;
-        case ONYX_INTRINSIC_I16X8_CONST:  SIMD_INT_CONST_INTRINSIC(u16, 8);   break;
-        case ONYX_INTRINSIC_I32X4_CONST:  SIMD_INT_CONST_INTRINSIC(u32, 4);   break;
-        case ONYX_INTRINSIC_I64X2_CONST:  SIMD_INT_CONST_INTRINSIC(u64, 2);   break;
-        case ONYX_INTRINSIC_F32X4_CONST: {
-            f32* byte_buffer = bh_alloc(mod->extended_instr_alloc, 16);
-            bh_arr(AstArgument *) arg_arr = (bh_arr(AstArgument *)) call->args.values;
-            fori (i, 0, 4) {
-                if (arg_arr[i]->value->kind != Ast_Kind_NumLit) {
-                    onyx_report_error(arg_arr[i]->token->pos, Error_Critical,
-                            "SIMD constants expect compile time constants as parameters. The %d%s parameter was not.",
-                            i, bh_num_suffix(i));
-                    *pcode = code;
-                    return;
-                }
-                byte_buffer[i] = (f32) ((AstNumLit *) arg_arr[i]->value)->value.f;
-            }
-            WIP(call->token, WI_V128_CONST, byte_buffer);
-            break;
-        }
-
-        case ONYX_INTRINSIC_F64X2_CONST: {
-            f64* byte_buffer = bh_alloc(mod->extended_instr_alloc, 16);
-            bh_arr(AstArgument *) arg_arr = (bh_arr(AstArgument *)) call->args.values;
-            fori (i, 0, 2) {
-                if (arg_arr[i]->value->kind != Ast_Kind_NumLit) {
-                    onyx_report_error(arg_arr[i]->token->pos, Error_Critical,
-                            "SIMD constants expect compile time constants as parameters. The %d%s parameter was not.",
-                            i, bh_num_suffix(i));
-                    *pcode = code;
-                    return;
-                }
-                byte_buffer[i] = (f64) ((AstNumLit *) arg_arr[i]->value)->value.d;
-            }
-            WIP(call->token, WI_V128_CONST, byte_buffer);
-            break;
-        }
-
-        case ONYX_INTRINSIC_I8X16_SHUFFLE: {
-            u8* byte_buffer = bh_alloc(mod->extended_instr_alloc, 16);
-            bh_arr(AstArgument *) arg_arr = (bh_arr(AstArgument *)) call->args.values;
-
-            // NOTE: There are two parameters that have to be outputted before
-            // the immediate bytes
-            emit_expression(mod, &code, arg_arr[0]->value);
-            emit_expression(mod, &code, arg_arr[1]->value);
-
-            fori (i, 0, 16) {
-                if (arg_arr[i + 2]->value->kind != Ast_Kind_NumLit) {
-                    onyx_report_error(arg_arr[i + 2]->token->pos, Error_Critical,
-                            "SIMD constants expect compile time constants as parameters. The %d%s parameter was not.",
-                            i, bh_num_suffix(i));
-                    *pcode = code;
-                    return;
-                }
-                byte_buffer[i] = (u8) ((AstNumLit *) arg_arr[i + 2]->value)->value.i;
-            }
-            WIP(call->token, WI_I8X16_SHUFFLE, byte_buffer);
-            break;
-        }
-
-        // CLEANUP ALL OF THIS
-        case ONYX_INTRINSIC_I8X16_EXTRACT_LANE_S: SIMD_EXTRACT_LANE_INSTR(WI_I8X16_EXTRACT_LANE_S, ((bh_arr(AstArgument *)) call->args.values)); break;
-        case ONYX_INTRINSIC_I8X16_EXTRACT_LANE_U: SIMD_EXTRACT_LANE_INSTR(WI_I8X16_EXTRACT_LANE_U, ((bh_arr(AstArgument *)) call->args.values)); break;
-        case ONYX_INTRINSIC_I8X16_REPLACE_LANE:   SIMD_REPLACE_LANE_INSTR(WI_I8X16_REPLACE_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
-        case ONYX_INTRINSIC_I16X8_EXTRACT_LANE_S: SIMD_EXTRACT_LANE_INSTR(WI_I16X8_EXTRACT_LANE_S, ((bh_arr(AstArgument *)) call->args.values)); break;
-        case ONYX_INTRINSIC_I16X8_EXTRACT_LANE_U: SIMD_EXTRACT_LANE_INSTR(WI_I16X8_EXTRACT_LANE_U, ((bh_arr(AstArgument *)) call->args.values)); break;
-        case ONYX_INTRINSIC_I16X8_REPLACE_LANE:   SIMD_REPLACE_LANE_INSTR(WI_I16X8_REPLACE_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
-        case ONYX_INTRINSIC_I32X4_EXTRACT_LANE:   SIMD_EXTRACT_LANE_INSTR(WI_I32X4_EXTRACT_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
-        case ONYX_INTRINSIC_I32X4_REPLACE_LANE:   SIMD_REPLACE_LANE_INSTR(WI_I32X4_REPLACE_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
-        case ONYX_INTRINSIC_I64X2_EXTRACT_LANE:   SIMD_EXTRACT_LANE_INSTR(WI_I64X2_EXTRACT_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
-        case ONYX_INTRINSIC_I64X2_REPLACE_LANE:   SIMD_REPLACE_LANE_INSTR(WI_I64X2_REPLACE_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
-        case ONYX_INTRINSIC_F32X4_EXTRACT_LANE:   SIMD_EXTRACT_LANE_INSTR(WI_F32X4_EXTRACT_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
-        case ONYX_INTRINSIC_F32X4_REPLACE_LANE:   SIMD_REPLACE_LANE_INSTR(WI_F32X4_REPLACE_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
-        case ONYX_INTRINSIC_F64X2_EXTRACT_LANE:   SIMD_EXTRACT_LANE_INSTR(WI_F64X2_EXTRACT_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
-        case ONYX_INTRINSIC_F64X2_REPLACE_LANE:   SIMD_REPLACE_LANE_INSTR(WI_F64X2_REPLACE_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
-
-        case ONYX_INTRINSIC_I8X16_SWIZZLE: WI(call->token, WI_I8X16_SWIZZLE); break;
-        case ONYX_INTRINSIC_I8X16_SPLAT:   WI(call->token, WI_I8X16_SPLAT); break;
-        case ONYX_INTRINSIC_I16X8_SPLAT:   WI(call->token, WI_I16X8_SPLAT); break;
-        case ONYX_INTRINSIC_I32X4_SPLAT:   WI(call->token, WI_I32X4_SPLAT); break;
-        case ONYX_INTRINSIC_I64X2_SPLAT:   WI(call->token, WI_I64X2_SPLAT); break;
-        case ONYX_INTRINSIC_F32X4_SPLAT:   WI(call->token, WI_F32X4_SPLAT); break;
-        case ONYX_INTRINSIC_F64X2_SPLAT:   WI(call->token, WI_F64X2_SPLAT); break;
-
-        case ONYX_INTRINSIC_I8X16_EQ:   WI(call->token, WI_I8X16_EQ); break;
-        case ONYX_INTRINSIC_I8X16_NEQ:  WI(call->token, WI_I8X16_NEQ); break;
-        case ONYX_INTRINSIC_I8X16_LT_S: WI(call->token, WI_I8X16_LT_S); break;
-        case ONYX_INTRINSIC_I8X16_LT_U: WI(call->token, WI_I8X16_LT_U); break;
-        case ONYX_INTRINSIC_I8X16_GT_S: WI(call->token, WI_I8X16_GT_S); break;
-        case ONYX_INTRINSIC_I8X16_GT_U: WI(call->token, WI_I8X16_GT_U); break;
-        case ONYX_INTRINSIC_I8X16_LE_S: WI(call->token, WI_I8X16_LE_S); break;
-        case ONYX_INTRINSIC_I8X16_LE_U: WI(call->token, WI_I8X16_LE_U); break;
-        case ONYX_INTRINSIC_I8X16_GE_S: WI(call->token, WI_I8X16_GE_S); break;
-        case ONYX_INTRINSIC_I8X16_GE_U: WI(call->token, WI_I8X16_GE_U); break;
-
-        case ONYX_INTRINSIC_I16X8_EQ:   WI(call->token, WI_I16X8_EQ); break;
-        case ONYX_INTRINSIC_I16X8_NEQ:  WI(call->token, WI_I16X8_NEQ); break;
-        case ONYX_INTRINSIC_I16X8_LT_S: WI(call->token, WI_I16X8_LT_S); break;
-        case ONYX_INTRINSIC_I16X8_LT_U: WI(call->token, WI_I16X8_LT_U); break;
-        case ONYX_INTRINSIC_I16X8_GT_S: WI(call->token, WI_I16X8_GT_S); break;
-        case ONYX_INTRINSIC_I16X8_GT_U: WI(call->token, WI_I16X8_GT_U); break;
-        case ONYX_INTRINSIC_I16X8_LE_S: WI(call->token, WI_I16X8_LE_S); break;
-        case ONYX_INTRINSIC_I16X8_LE_U: WI(call->token, WI_I16X8_LE_U); break;
-        case ONYX_INTRINSIC_I16X8_GE_S: WI(call->token, WI_I16X8_GE_S); break;
-        case ONYX_INTRINSIC_I16X8_GE_U: WI(call->token, WI_I16X8_GE_U); break;
-
-        case ONYX_INTRINSIC_I32X4_EQ:   WI(call->token, WI_I32X4_EQ); break;
-        case ONYX_INTRINSIC_I32X4_NEQ:  WI(call->token, WI_I32X4_NEQ); break;
-        case ONYX_INTRINSIC_I32X4_LT_S: WI(call->token, WI_I32X4_LT_S); break;
-        case ONYX_INTRINSIC_I32X4_LT_U: WI(call->token, WI_I32X4_LT_U); break;
-        case ONYX_INTRINSIC_I32X4_GT_S: WI(call->token, WI_I32X4_GT_S); break;
-        case ONYX_INTRINSIC_I32X4_GT_U: WI(call->token, WI_I32X4_GT_U); break;
-        case ONYX_INTRINSIC_I32X4_LE_S: WI(call->token, WI_I32X4_LE_S); break;
-        case ONYX_INTRINSIC_I32X4_LE_U: WI(call->token, WI_I32X4_LE_U); break;
-        case ONYX_INTRINSIC_I32X4_GE_S: WI(call->token, WI_I32X4_GE_S); break;
-        case ONYX_INTRINSIC_I32X4_GE_U: WI(call->token, WI_I32X4_GE_U); break;
-
-        case ONYX_INTRINSIC_F32X4_EQ:  WI(call->token, WI_F32X4_EQ); break;
-        case ONYX_INTRINSIC_F32X4_NEQ: WI(call->token, WI_F32X4_NEQ); break;
-        case ONYX_INTRINSIC_F32X4_LT:  WI(call->token, WI_F32X4_LT); break;
-        case ONYX_INTRINSIC_F32X4_GT:  WI(call->token, WI_F32X4_GT); break;
-        case ONYX_INTRINSIC_F32X4_LE:  WI(call->token, WI_F32X4_LE); break;
-        case ONYX_INTRINSIC_F32X4_GE:  WI(call->token, WI_F32X4_GE); break;
-
-        case ONYX_INTRINSIC_F64X2_EQ:  WI(call->token, WI_F64X2_EQ); break;
-        case ONYX_INTRINSIC_F64X2_NEQ: WI(call->token, WI_F64X2_NEQ); break;
-        case ONYX_INTRINSIC_F64X2_LT:  WI(call->token, WI_F64X2_LT); break;
-        case ONYX_INTRINSIC_F64X2_GT:  WI(call->token, WI_F64X2_GT); break;
-        case ONYX_INTRINSIC_F64X2_LE:  WI(call->token, WI_F64X2_LE); break;
-        case ONYX_INTRINSIC_F64X2_GE:  WI(call->token, WI_F64X2_GE); break;
-
-        case ONYX_INTRINSIC_V128_NOT:       WI(call->token, WI_V128_NOT); break;
-        case ONYX_INTRINSIC_V128_AND:       WI(call->token, WI_V128_AND); break;
-        case ONYX_INTRINSIC_V128_ANDNOT:    WI(call->token, WI_V128_ANDNOT); break;
-        case ONYX_INTRINSIC_V128_OR:        WI(call->token, WI_V128_OR); break;
-        case ONYX_INTRINSIC_V128_XOR:       WI(call->token, WI_V128_XOR); break;
-        case ONYX_INTRINSIC_V128_BITSELECT: WI(call->token, WI_V128_BITSELECT); break;
-
-        case ONYX_INTRINSIC_I8X16_ABS:            WI(call->token, WI_I8X16_ABS); break;
-        case ONYX_INTRINSIC_I8X16_NEG:            WI(call->token, WI_I8X16_NEG); break;
-        case ONYX_INTRINSIC_I8X16_ANY_TRUE:       WI(call->token, WI_I8X16_ANY_TRUE); break;
-        case ONYX_INTRINSIC_I8X16_ALL_TRUE:       WI(call->token, WI_I8X16_ALL_TRUE); break;
-        case ONYX_INTRINSIC_I8X16_BITMASK:        WI(call->token, WI_I8X16_BITMASK); break;
-        case ONYX_INTRINSIC_I8X16_NARROW_I16X8_S: WI(call->token, WI_I8X16_NARROW_I16X8_S); break;
-        case ONYX_INTRINSIC_I8X16_NARROW_I16X8_U: WI(call->token, WI_I8X16_NARROW_I16X8_U); break;
-        case ONYX_INTRINSIC_I8X16_SHL:            WI(call->token, WI_I8X16_SHL); break;
-        case ONYX_INTRINSIC_I8X16_SHR_S:          WI(call->token, WI_I8X16_SHR_S); break;
-        case ONYX_INTRINSIC_I8X16_SHR_U:          WI(call->token, WI_I8X16_SHR_U); break;
-        case ONYX_INTRINSIC_I8X16_ADD:            WI(call->token, WI_I8X16_ADD); break;
-        case ONYX_INTRINSIC_I8X16_ADD_SAT_S:      WI(call->token, WI_I8X16_ADD_SAT_S); break;
-        case ONYX_INTRINSIC_I8X16_ADD_SAT_U:      WI(call->token, WI_I8X16_ADD_SAT_U); break;
-        case ONYX_INTRINSIC_I8X16_SUB:            WI(call->token, WI_I8X16_SUB); break;
-        case ONYX_INTRINSIC_I8X16_SUB_SAT_S:      WI(call->token, WI_I8X16_SUB_SAT_S); break;
-        case ONYX_INTRINSIC_I8X16_SUB_SAT_U:      WI(call->token, WI_I8X16_SUB_SAT_U); break;
-        case ONYX_INTRINSIC_I8X16_MIN_S:          WI(call->token, WI_I8X16_MIN_S); break;
-        case ONYX_INTRINSIC_I8X16_MIN_U:          WI(call->token, WI_I8X16_MIN_U); break;
-        case ONYX_INTRINSIC_I8X16_MAX_S:          WI(call->token, WI_I8X16_MAX_S); break;
-        case ONYX_INTRINSIC_I8X16_MAX_U:          WI(call->token, WI_I8X16_MAX_U); break;
-        case ONYX_INTRINSIC_I8X16_AVGR_U:         WI(call->token, WI_I8X16_AVGR_U); break;
-
-        case ONYX_INTRINSIC_I16X8_ABS:                WI(call->token, WI_I16X8_ABS); break;
-        case ONYX_INTRINSIC_I16X8_NEG:                WI(call->token, WI_I16X8_NEG); break;
-        case ONYX_INTRINSIC_I16X8_ANY_TRUE:           WI(call->token, WI_I16X8_ANY_TRUE); break;
-        case ONYX_INTRINSIC_I16X8_ALL_TRUE:           WI(call->token, WI_I16X8_ALL_TRUE); break;
-        case ONYX_INTRINSIC_I16X8_BITMASK:            WI(call->token, WI_I16X8_BITMASK); break;
-        case ONYX_INTRINSIC_I16X8_NARROW_I32X4_S:     WI(call->token, WI_I16X8_NARROW_I32X4_S); break;
-        case ONYX_INTRINSIC_I16X8_NARROW_I32X4_U:     WI(call->token, WI_I16X8_NARROW_I32X4_U); break;
-        case ONYX_INTRINSIC_I16X8_WIDEN_LOW_I8X16_S:  WI(call->token, WI_I16X8_WIDEN_LOW_I8X16_S); break;
-        case ONYX_INTRINSIC_I16X8_WIDEN_HIGH_I8X16_S: WI(call->token, WI_I16X8_WIDEN_HIGH_I8X16_S); break;
-        case ONYX_INTRINSIC_I16X8_WIDEN_LOW_I8X16_U:  WI(call->token, WI_I16X8_WIDEN_LOW_I8X16_U); break;
-        case ONYX_INTRINSIC_I16X8_WIDEN_HIGH_I8X16_U: WI(call->token, WI_I16X8_WIDEN_HIGH_I8X16_U); break;
-        case ONYX_INTRINSIC_I16X8_SHL:                WI(call->token, WI_I16X8_SHL); break;
-        case ONYX_INTRINSIC_I16X8_SHR_S:              WI(call->token, WI_I16X8_SHR_S); break;
-        case ONYX_INTRINSIC_I16X8_SHR_U:              WI(call->token, WI_I16X8_SHR_U); break;
-        case ONYX_INTRINSIC_I16X8_ADD:                WI(call->token, WI_I16X8_ADD); break;
-        case ONYX_INTRINSIC_I16X8_ADD_SAT_S:          WI(call->token, WI_I16X8_ADD_SAT_S); break;
-        case ONYX_INTRINSIC_I16X8_ADD_SAT_U:          WI(call->token, WI_I16X8_ADD_SAT_U); break;
-        case ONYX_INTRINSIC_I16X8_SUB:                WI(call->token, WI_I16X8_SUB); break;
-        case ONYX_INTRINSIC_I16X8_SUB_SAT_S:          WI(call->token, WI_I16X8_SUB_SAT_S); break;
-        case ONYX_INTRINSIC_I16X8_SUB_SAT_U:          WI(call->token, WI_I16X8_SUB_SAT_U); break;
-        case ONYX_INTRINSIC_I16X8_MUL:                WI(call->token, WI_I16X8_MUL); break;
-        case ONYX_INTRINSIC_I16X8_MIN_S:              WI(call->token, WI_I16X8_MIN_S); break;
-        case ONYX_INTRINSIC_I16X8_MIN_U:              WI(call->token, WI_I16X8_MIN_U); break;
-        case ONYX_INTRINSIC_I16X8_MAX_S:              WI(call->token, WI_I16X8_MAX_S); break;
-        case ONYX_INTRINSIC_I16X8_MAX_U:              WI(call->token, WI_I16X8_MAX_U); break;
-        case ONYX_INTRINSIC_I16X8_AVGR_U:             WI(call->token, WI_I16X8_AVGR_U); break;
-
-        case ONYX_INTRINSIC_I32X4_ABS:                WI(call->token, WI_I32X4_ABS); break;
-        case ONYX_INTRINSIC_I32X4_NEG:                WI(call->token, WI_I32X4_NEG); break;
-        case ONYX_INTRINSIC_I32X4_ANY_TRUE:           WI(call->token, WI_I32X4_ANY_TRUE); break;
-        case ONYX_INTRINSIC_I32X4_ALL_TRUE:           WI(call->token, WI_I32X4_ALL_TRUE); break;
-        case ONYX_INTRINSIC_I32X4_BITMASK:            WI(call->token, WI_I32X4_BITMASK); break;
-        case ONYX_INTRINSIC_I32X4_WIDEN_LOW_I16X8_S:  WI(call->token, WI_I32X4_WIDEN_LOW_I16X8_S); break;
-        case ONYX_INTRINSIC_I32X4_WIDEN_HIGH_I16X8_S: WI(call->token, WI_I32X4_WIDEN_HIGH_I16X8_S); break;
-        case ONYX_INTRINSIC_I32X4_WIDEN_LOW_I16X8_U:  WI(call->token, WI_I32X4_WIDEN_LOW_I16X8_U); break;
-        case ONYX_INTRINSIC_I32X4_WIDEN_HIGH_I16X8_U: WI(call->token, WI_I32X4_WIDEN_HIGH_I16X8_U); break;
-        case ONYX_INTRINSIC_I32X4_SHL:                WI(call->token, WI_I32X4_SHL); break;
-        case ONYX_INTRINSIC_I32X4_SHR_S:              WI(call->token, WI_I32X4_SHR_S); break;
-        case ONYX_INTRINSIC_I32X4_SHR_U:              WI(call->token, WI_I32X4_SHR_U); break;
-        case ONYX_INTRINSIC_I32X4_ADD:                WI(call->token, WI_I32X4_ADD); break;
-        case ONYX_INTRINSIC_I32X4_SUB:                WI(call->token, WI_I32X4_SUB); break;
-        case ONYX_INTRINSIC_I32X4_MUL:                WI(call->token, WI_I32X4_MUL); break;
-        case ONYX_INTRINSIC_I32X4_MIN_S:              WI(call->token, WI_I32X4_MIN_S); break;
-        case ONYX_INTRINSIC_I32X4_MIN_U:              WI(call->token, WI_I32X4_MIN_U); break;
-        case ONYX_INTRINSIC_I32X4_MAX_S:              WI(call->token, WI_I32X4_MAX_S); break;
-        case ONYX_INTRINSIC_I32X4_MAX_U:              WI(call->token, WI_I32X4_MAX_U); break;
-
-        case ONYX_INTRINSIC_I64X2_NEG:   WI(call->token, WI_I64X2_NEG); break;
-        case ONYX_INTRINSIC_I64X2_SHL:   WI(call->token, WI_I64X2_SHL); break;
-        case ONYX_INTRINSIC_I64X2_SHR_S: WI(call->token, WI_I64X2_SHR_S); break;
-        case ONYX_INTRINSIC_I64X2_SHR_U: WI(call->token, WI_I64X2_SHR_U); break;
-        case ONYX_INTRINSIC_I64X2_ADD:   WI(call->token, WI_I64X2_ADD); break;
-        case ONYX_INTRINSIC_I64X2_SUB:   WI(call->token, WI_I64X2_SUB); break;
-        case ONYX_INTRINSIC_I64X2_MUL:   WI(call->token, WI_I64X2_MUL); break;
-
-        case ONYX_INTRINSIC_F32X4_ABS:  WI(call->token, WI_F32X4_ABS); break;
-        case ONYX_INTRINSIC_F32X4_NEG:  WI(call->token, WI_F32X4_NEG); break;
-        case ONYX_INTRINSIC_F32X4_SQRT: WI(call->token, WI_F32X4_SQRT); break;
-        case ONYX_INTRINSIC_F32X4_ADD:  WI(call->token, WI_F32X4_ADD); break;
-        case ONYX_INTRINSIC_F32X4_SUB:  WI(call->token, WI_F32X4_SUB); break;
-        case ONYX_INTRINSIC_F32X4_MUL:  WI(call->token, WI_F32X4_MUL); break;
-        case ONYX_INTRINSIC_F32X4_DIV:  WI(call->token, WI_F32X4_DIV); break;
-        case ONYX_INTRINSIC_F32X4_MIN:  WI(call->token, WI_F32X4_MIN); break;
-        case ONYX_INTRINSIC_F32X4_MAX:  WI(call->token, WI_F32X4_MAX); break;
-
-        case ONYX_INTRINSIC_F64X2_ABS:  WI(call->token, WI_F64X2_ABS); break;
-        case ONYX_INTRINSIC_F64X2_NEG:  WI(call->token, WI_F64X2_NEG); break;
-        case ONYX_INTRINSIC_F64X2_SQRT: WI(call->token, WI_F64X2_SQRT); break;
-        case ONYX_INTRINSIC_F64X2_ADD:  WI(call->token, WI_F64X2_ADD); break;
-        case ONYX_INTRINSIC_F64X2_SUB:  WI(call->token, WI_F64X2_SUB); break;
-        case ONYX_INTRINSIC_F64X2_MUL:  WI(call->token, WI_F64X2_MUL); break;
-        case ONYX_INTRINSIC_F64X2_DIV:  WI(call->token, WI_F64X2_DIV); break;
-        case ONYX_INTRINSIC_F64X2_MIN:  WI(call->token, WI_F64X2_MIN); break;
-        case ONYX_INTRINSIC_F64X2_MAX:  WI(call->token, WI_F64X2_MAX); break;
-
-        case ONYX_INTRINSIC_I32X4_TRUNC_SAT_F32X4_S: WI(call->token, WI_I32X4_TRUNC_SAT_F32X4_S); break;
-        case ONYX_INTRINSIC_I32X4_TRUNC_SAT_F32X4_U: WI(call->token, WI_I32X4_TRUNC_SAT_F32X4_U); break;
-        case ONYX_INTRINSIC_F32X4_CONVERT_I32X4_S:   WI(call->token, WI_F32X4_CONVERT_I32X4_S); break;
-        case ONYX_INTRINSIC_F32X4_CONVERT_I32X4_U:   WI(call->token, WI_F32X4_CONVERT_I32X4_U); break;
-
-        case ONYX_INTRINSIC_ATOMIC_WAIT: {
-            Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
-            emit_intrinsic_atomic_wait(mod, &code, atomic_type, call->token);
-            break;
-        }
-
-        case ONYX_INTRINSIC_ATOMIC_NOTIFY: {
-            emit_intrinsic_atomic_notify(mod, &code);
-            break;
-        }
-
-        case ONYX_INTRINSIC_ATOMIC_FENCE: {
-            emit_intrinsic_atomic_fence(mod, &code);
-            break;
-        }
-
-        case ONYX_INTRINSIC_ATOMIC_LOAD: {
-            Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
-            emit_intrinsic_atomic_load(mod, &code, atomic_type, call->token);
-            break;
-        }
-
-        case ONYX_INTRINSIC_ATOMIC_STORE: {
-            Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
-            emit_intrinsic_atomic_store(mod, &code, atomic_type, call->token);
-            break;
-        }
-
-        case ONYX_INTRINSIC_ATOMIC_ADD: {
-            Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
-            emit_intrinsic_atomic_add(mod, &code, atomic_type, call->token);
-            break;
-        }
-
-        case ONYX_INTRINSIC_ATOMIC_SUB: {
-            Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
-            emit_intrinsic_atomic_sub(mod, &code, atomic_type, call->token);
-            break;
-        }
-
-        case ONYX_INTRINSIC_ATOMIC_AND: {
-            Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
-            emit_intrinsic_atomic_and(mod, &code, atomic_type, call->token);
-            break;
-        }
-
-        case ONYX_INTRINSIC_ATOMIC_OR: {
-            Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
-            emit_intrinsic_atomic_or(mod, &code, atomic_type, call->token);
-            break;
-        }
-
-        case ONYX_INTRINSIC_ATOMIC_XOR: {
-            Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
-            emit_intrinsic_atomic_xor(mod, &code, atomic_type, call->token);
-            break;
-        }
-
-        case ONYX_INTRINSIC_ATOMIC_XCHG: {
-            Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
-            emit_intrinsic_atomic_xchg(mod, &code, atomic_type, call->token);
-            break;
-        }
-
-        case ONYX_INTRINSIC_ATOMIC_CMPXCHG: {
-            Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
-            emit_intrinsic_atomic_cmpxchg(mod, &code, atomic_type, call->token);
-            break;
-        }
-
-        default: assert(("Unsupported intrinsic", 0));
-    }
-
-    *pcode = code;
-}
-
-EMIT_FUNC(subscript_location, AstSubscript* sub, u64* offset_return) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    emit_expression(mod, &code, sub->expr);
-    if (sub->elem_size != 1) {
-        WID(sub->token, WI_PTR_CONST, sub->elem_size);
-        WI(sub->token, WI_PTR_MUL);
-    }
-
-    // CLEANUP: This is one dense clusterf**k of code...
-    u64 offset = 0;
-    if (sub->addr->kind == Ast_Kind_Subscript
-        && sub->addr->type->kind == Type_Kind_Array) {
-        emit_subscript_location(mod, &code, (AstSubscript *) sub->addr, &offset);
-    } else if (sub->addr->kind == Ast_Kind_Field_Access
-        && sub->addr->type->kind == Type_Kind_Array) {
-        emit_field_access_location(mod, &code, (AstFieldAccess *) sub->addr, &offset);
-    } else if ((sub->addr->kind == Ast_Kind_Local || sub->addr->kind == Ast_Kind_Param)
-        && sub->addr->type->kind == Type_Kind_Array) {
-        emit_local_location(mod, &code, (AstLocal *) sub->addr, &offset);
-    } else if (sub->addr->kind == Ast_Kind_Memres
-        && sub->addr->type->kind != Type_Kind_Array) {
-        emit_memory_reservation_location(mod, &code, (AstMemRes *) sub->addr);
-    } else {
-        emit_expression(mod, &code, sub->addr);
-    }
-
-    WI(sub->token, WI_PTR_ADD);
-
-    *offset_return += offset;
-
-    *pcode = code;
-}
-
-EMIT_FUNC(field_access_location, AstFieldAccess* field, u64* offset_return) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    u64 offset = field->offset;
-    AstTyped* source_expr = field->expr;
-    while (source_expr->kind == Ast_Kind_Field_Access
-            && type_is_structlike_strict(source_expr->type)) {
-        offset += ((AstFieldAccess *) source_expr)->offset;
-        source_expr = (AstTyped *) ((AstFieldAccess *) source_expr)->expr;
-    }
-
-    if (source_expr->kind == Ast_Kind_Subscript
-        && source_expr->type->kind != Type_Kind_Pointer) {
-        u64 o2 = 0;
-        emit_subscript_location(mod, &code, (AstSubscript *) source_expr, &o2);
-        offset += o2;
-
-    } else if ((source_expr->kind == Ast_Kind_Local || source_expr->kind == Ast_Kind_Param)
-        && source_expr->type->kind != Type_Kind_Pointer) {
-        u64 o2 = 0;
-        emit_local_location(mod, &code, (AstLocal *) source_expr, &o2);
-        offset += o2;
-
-    } else if (source_expr->kind == Ast_Kind_Memres
-        && source_expr->type->kind != Type_Kind_Pointer) {
-        emit_memory_reservation_location(mod, &code, (AstMemRes *) source_expr);
-
-    } else {
-        emit_expression(mod, &code, source_expr);
-    }
-
-    *offset_return = offset;
-
-    *pcode = code;
-}
-
-EMIT_FUNC(memory_reservation_location, AstMemRes* memres) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    if (memres->threadlocal) {
-        u64 tls_base_idx = bh_imap_get(&mod->index_map, (u64) &builtin_tls_base);
-        WID(NULL, WI_PTR_CONST, memres->tls_offset);
-        WIL(NULL, WI_GLOBAL_GET, tls_base_idx);
-        WI(NULL, WI_PTR_ADD);
-
-    } else {
-        // :ProperLinking
-        assert(memres->data_id != 0);
-        emit_data_relocation(mod, &code, memres->data_id);
-    }
-
-    *pcode = code;
-}
-
-EMIT_FUNC(local_location, AstLocal* local, u64* offset_return) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    u64 local_offset = (u64) bh_imap_get(&mod->local_map, (u64) local);
-
-    if (local_offset & LOCAL_IS_WASM) {
-        // This is a weird condition but it is relied on in a couple places including
-        // passing non-simple structs by value.             -brendanfh 2020/09/18
-        WIL(NULL, WI_LOCAL_GET, local_offset);
-
-    } else {
-        WIL(NULL, WI_LOCAL_GET, mod->stack_base_idx);
-
-        *offset_return += local_offset;
-    }
-
-    *pcode = code;
-}
-
-EMIT_FUNC(struct_lval, AstTyped* lval) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    assert(type_is_structlike_strict(lval->type));
-
-    u64 offset = 0;
-    emit_location_return_offset(mod, &code, lval, &offset);
-    emit_compound_store(mod, &code, lval->type, offset, 1);
-
-    *pcode = code;
-}
-
-EMIT_FUNC(compound_load, Type* type, u64 offset) {
-    bh_arr(WasmInstruction) code = *pcode;
-    i32 mem_count = type_linear_member_count(type);
-    TypeWithOffset two;
-
-    if (mem_count == 1) {
-        type_linear_member_lookup(type, 0, &two);
-        emit_load_instruction(mod, &code, two.type, offset + two.offset); // two.offset should be 0
-    } else {
-        u64 tmp_idx = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
-        WIL(NULL, WI_LOCAL_TEE, tmp_idx);
-
-        fori (i, 0, mem_count) {
-            type_linear_member_lookup(type, i, &two);
-            if (i != 0) WIL(NULL, WI_LOCAL_GET, tmp_idx);
-            emit_load_instruction(mod, &code, two.type, offset + two.offset);
-        }
-
-        local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
-    }
-
-    *pcode = code;
-    return;
-}
-
-EMIT_FUNC(compound_store, Type* type, u64 offset, b32 location_first) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    TypeWithOffset two;
-
-    u64 loc_idx = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
-    if (location_first) WIL(NULL, WI_LOCAL_SET, loc_idx);
-
-    i32 elem_count = type_linear_member_count(type);
-    u64 *temp_locals = bh_alloc_array(global_scratch_allocator, u64, elem_count);
-
-    forir (i, elem_count - 1, 0) {
-        type_linear_member_lookup(type, i, &two);
-
-        WasmType wt = onyx_type_to_wasm_type(two.type);
-        temp_locals[i] = local_raw_allocate(mod->local_alloc, wt);
-        WIL(NULL, WI_LOCAL_SET, temp_locals[i]);
-    }
-
-    if (!location_first) WIL(NULL, WI_LOCAL_SET, loc_idx);
-
-    fori (i, 0, elem_count) {
-        type_linear_member_lookup(type, i, &two);
-
-        u64 tmp_idx = temp_locals[i];
-        WIL(NULL, WI_LOCAL_GET, loc_idx);
-        WIL(NULL, WI_LOCAL_GET, tmp_idx);
-        emit_store_instruction(mod, &code, two.type, offset + two.offset);
-
-        WasmType wt = onyx_type_to_wasm_type(two.type);
-        local_raw_free(mod->local_alloc, wt);
-    }
-
-    local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
-
-    // This shouldn't be necessary because the scratch allocator doesn't free.
-    bh_free(global_scratch_allocator, temp_locals);
-
-    *pcode = code;
-}
-
-EMIT_FUNC(struct_literal, AstStructLiteral* sl) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    bh_arr_each(AstTyped *, val, sl->args.values) {
-        emit_expression(mod, &code, *val);
-    }
-
-    *pcode = code;
-}
-
-EMIT_FUNC(array_store, Type* type, u32 offset) {
-    assert(type->kind == Type_Kind_Array);
-    bh_arr(WasmInstruction) code = *pcode;
-
-    Type* elem_type = type;
-    u32 elem_count = 1;
-    while (elem_type->kind == Type_Kind_Array) {
-        elem_count *= elem_type->Array.count;
-        elem_type = elem_type->Array.elem;
-    }
-    u32 elem_size = type_size_of(elem_type);
-
-    u64 lptr_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
-    u64 rptr_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
-    WIL(NULL, WI_LOCAL_SET, rptr_local);
-    WIL(NULL, WI_LOCAL_SET, lptr_local);
-
-    WIL(NULL, WI_LOCAL_GET, rptr_local);
-    WID(NULL, WI_I32_CONST, 0);
-    WI(NULL, WI_I32_NE);
-    emit_enter_structured_block(mod, &code, SBT_Basic_If, NULL);
-
-    //
-    // CLEANUP: Most of these cases could be much shorter if they used existing intrinsics.
-    //
-    if (elem_count <= 2) {
-        // Inline copying for a small number of elements. It still may be faster to do this in a tight loop.
-
-        fori (i, 0, elem_count) {
-            if (bh_arr_last(code).type == WI_LOCAL_SET && (u64) bh_arr_last(code).data.l == lptr_local)
-                bh_arr_last(code).type = WI_LOCAL_TEE;
-            else
-                WIL(NULL, WI_LOCAL_GET, lptr_local);
-
-            WIL(NULL, WI_LOCAL_GET, rptr_local);
-            emit_load_instruction(mod, &code, elem_type, i * elem_size);
-
-            emit_store_instruction(mod, &code, elem_type, i * elem_size + offset);
-        }
-
-    } else if (context.options->use_post_mvp_features) {
-        // Use a simple memory copy if it is available. This may be what happens in the case below too at a later time.
-
-        if (bh_arr_last(code).type == WI_LOCAL_SET && (u64) bh_arr_last(code).data.l == lptr_local)
-            bh_arr_last(code).type = WI_LOCAL_TEE;
-        else
-            WIL(NULL, WI_LOCAL_GET, lptr_local);
-
-        if (offset != 0) {
-            WIL(NULL, WI_PTR_CONST, offset);
-            WI(NULL, WI_PTR_ADD);
-        }
-
-        WIL(NULL, WI_LOCAL_GET, rptr_local);
-        WIL(NULL, WI_I32_CONST, elem_count * elem_size);
-        WI(NULL, WI_MEMORY_COPY);
-
-    } else {
-        // Emit a loop that copies the memory. This could be switched to a tight loop that just copies word per word.
-
-        u64 offset_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
-        WIL(NULL, WI_PTR_CONST, 0);
-        WIL(NULL, WI_LOCAL_SET, offset_local);
-
-        WID(NULL, WI_BLOCK_START, 0x40);
-        WID(NULL, WI_LOOP_START, 0x40);
-            WIL(NULL, WI_LOCAL_GET, offset_local);
-            WIL(NULL, WI_LOCAL_GET, lptr_local);
-            WI(NULL, WI_PTR_ADD);
-
-            WIL(NULL, WI_LOCAL_GET, offset_local);
-            WIL(NULL, WI_LOCAL_GET, rptr_local);
-            WI(NULL, WI_PTR_ADD);
-
-            emit_load_instruction(mod, &code, elem_type, 0);
-            emit_store_instruction(mod, &code, elem_type, offset);
-
-            WIL(NULL, WI_LOCAL_GET, offset_local);
-            WIL(NULL, WI_PTR_CONST, elem_size);
-            WI(NULL, WI_PTR_ADD);
-            WIL(NULL, WI_LOCAL_TEE, offset_local);
-
-            WIL(NULL, WI_PTR_CONST, elem_count * elem_size);
-            WI(NULL, WI_PTR_GE);
-            WID(NULL, WI_COND_JUMP, 0x01);
-
-            WID(NULL, WI_JUMP, 0x00);
-
-        WI(NULL, WI_LOOP_END);
-        WI(NULL, WI_BLOCK_END);
-
-        local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
-    }
-
-    WI(NULL, WI_ELSE);
-
-    { // If the source ptr is null (0), then just copy in 0 bytes.
-        WIL(NULL, WI_LOCAL_GET, lptr_local);
-        if (offset != 0) {
-            WIL(NULL, WI_PTR_CONST, offset);
-            WI(NULL, WI_PTR_ADD);
-        }
-
-        WIL(NULL, WI_I32_CONST, 0);
-
-        WIL(NULL, WI_I32_CONST, elem_count * elem_size);
-
-        if (context.options->use_post_mvp_features) {
-            WI(NULL, WI_MEMORY_FILL);
-        } else {
-            emit_intrinsic_memory_fill(mod, &code);
-        }
-    }
-
-    local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
-    local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
-
-    emit_leave_structured_block(mod, &code);
-    *pcode = code;
-    return;
-}
-
-EMIT_FUNC(array_literal, AstArrayLiteral* al) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    u64 local_offset = (u64) bh_imap_get(&mod->local_map, (u64) al);
-    assert((local_offset & LOCAL_IS_WASM) == 0);
-
-    assert(al->type->kind == Type_Kind_Array);
-
-    u32 elem_size = type_size_of(al->type->Array.elem);
-
-    fori (i, 0, al->type->Array.count) {
-        WIL(al->token, WI_LOCAL_GET, mod->stack_base_idx);
-        emit_expression(mod, &code, al->values[i]);
-        emit_store_instruction(mod, &code, al->type->Array.elem, local_offset + i * elem_size);
-    }
-
-    WIL(al->token, WI_LOCAL_GET, mod->stack_base_idx);
-    WIL(al->token, WI_PTR_CONST, local_offset);
-    WI(al->token, WI_PTR_ADD);
-
-    *pcode = code;
-}
-
-EMIT_FUNC(range_literal, AstRangeLiteral* range) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    emit_expression(mod, &code, range->low);
-    emit_expression(mod, &code, range->high);
-    emit_expression(mod, &code, range->step);
-
-    *pcode = code;
-}
-
-EMIT_FUNC(if_expression, AstIfExpression* if_expr) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    u64 offset = 0;
-    u64 result_local    = local_allocate(mod->local_alloc, (AstTyped *) if_expr);
-    b32 result_is_local = (b32) ((result_local & LOCAL_IS_WASM) != 0);
-    bh_imap_put(&mod->local_map, (u64) if_expr, result_local);
-
-    emit_expression(mod, &code, if_expr->cond);
-
-    emit_enter_structured_block(mod, &code, SBT_Basic_If, if_expr->token);
-        if (!result_is_local) emit_local_location(mod, &code, (AstLocal *) if_expr, &offset);
-
-        emit_expression(mod, &code, if_expr->true_expr);
-
-        if (!result_is_local) emit_store_instruction(mod, &code, if_expr->type, offset);
-        else                  WIL(if_expr->token, WI_LOCAL_SET, result_local);
-
-    offset = 0;
-    WI(if_expr->token, WI_ELSE);
-        if (!result_is_local) emit_local_location(mod, &code, (AstLocal *) if_expr, &offset);
-
-        emit_expression(mod, &code, if_expr->false_expr);
-
-        if (!result_is_local) emit_store_instruction(mod, &code, if_expr->type, offset);
-        else                  WIL(if_expr->token, WI_LOCAL_SET, result_local);
-
-    emit_leave_structured_block(mod, &code);
-
-    offset = 0;
-    if (!result_is_local) {
-        emit_local_location(mod, &code, (AstLocal *) if_expr, &offset);
-        emit_load_instruction(mod, &code, if_expr->type, offset);
-
-    } else {
-        WIL(if_expr->token, WI_LOCAL_GET, result_local);
-    }
-
-    local_free(mod->local_alloc, (AstTyped *) if_expr);
-
-    *pcode = code;
-}
-
-EMIT_FUNC(do_block, AstDoBlock* doblock) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    u64 result_local    = local_allocate(mod->local_alloc, (AstTyped *) doblock);
-    b32 result_is_local = (b32) ((result_local & LOCAL_IS_WASM) != 0);
-
-    bh_imap_put(&mod->local_map, (u64) doblock, result_local);
-    bh_arr_push(mod->return_location_stack, (AstLocal *) doblock);
-
-    emit_block(mod, &code, doblock->block, 1);
-
-    u64 offset = 0;
-    if (!result_is_local) {
-        emit_local_location(mod, &code, (AstLocal *) doblock, &offset);
-        emit_load_instruction(mod, &code, doblock->type, offset);
-
-    } else {
-        WIL(doblock->block->token, WI_LOCAL_GET, result_local);
-    }
-
-    bh_arr_pop(mod->return_location_stack);
-    local_free(mod->local_alloc, (AstTyped *) doblock);
-
-    *pcode = code;
-}
-
-EMIT_FUNC(location_return_offset, AstTyped* expr, u64* offset_return) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    expr = (AstTyped *) strip_aliases((AstNode *) expr);
-
-    switch (expr->kind) {
-        case Ast_Kind_Param:
-        case Ast_Kind_Local:
-        case Ast_Kind_Array_Literal:
-        case Ast_Kind_Struct_Literal: {
-            emit_local_location(mod, &code, (AstLocal *) expr, offset_return);
-            break;
-        }
-
-        case Ast_Kind_Dereference: {
-            emit_expression(mod, &code, ((AstDereference *) expr)->expr);
-            *offset_return = 0;
-            break;
-        }
-
-        case Ast_Kind_Subscript: {
-            AstSubscript* sub = (AstSubscript *) expr;
-            emit_subscript_location(mod, &code, sub, offset_return);
-            break;
-        }
-
-        case Ast_Kind_Field_Access: {
-            AstFieldAccess* field = (AstFieldAccess *) expr;
-            emit_field_access_location(mod, &code, field, offset_return);
-            break;
-        }
-
-        case Ast_Kind_Memres: {
-            AstMemRes* memres = (AstMemRes *) expr;
-            emit_memory_reservation_location(mod, &code, memres);
-            *offset_return = 0;
-            break;
-        }
-
-        default: {
-            onyx_report_error(expr->token->pos, Error_Critical, "Unable to generate location for '%s'.", onyx_ast_node_kind_string(expr->kind));
-            break;
-        }
-    }
-
-    *pcode = code;
-}
-
-EMIT_FUNC(location, AstTyped* expr) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    u64 offset = 0;
-    emit_location_return_offset(mod, &code, expr, &offset);
-    if (offset != 0) {
-        WID(NULL, WI_PTR_CONST, offset);
-        WI(NULL, WI_PTR_ADD);
-    }
-
-    *pcode = code;
-}
-
-EMIT_FUNC(expression, AstTyped* expr) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    if (node_is_type((AstNode *) expr)) {
-        AstType* type = (AstType *) expr;
-        if (type->flags & Ast_Flag_Expr_Ignored) return;
-
-        if (type->type_id != 0) {
-            WID(NULL, WI_I32_CONST, ((AstType *) expr)->type_id);
-        } else {
-            Type* t = type_build_from_ast(context.ast_alloc, type);
-            WID(NULL, WI_I32_CONST, t->id);
-        }
-
-
-        *pcode = code;
-        return;
-    }
-
-    switch (expr->kind) {
-        case Ast_Kind_Param: {
-            AstLocal* param = (AstLocal *) expr;
-            u64 localidx = bh_imap_get(&mod->local_map, (u64) param);
-
-            switch (type_get_param_pass(param->type)) {
-                case Param_Pass_By_Value: {
-                    if (type_is_structlike_strict(expr->type)) {
-                        u32 mem_count = type_structlike_mem_count(expr->type);
-                        fori (idx, 0, mem_count) WIL(NULL, WI_LOCAL_GET, localidx + idx);
-
-                    } else {
-                        assert(localidx & LOCAL_IS_WASM);
-                        WIL(NULL, WI_LOCAL_GET, localidx);
-                    }
-                    break;
-                }
-
-                case Param_Pass_By_Implicit_Pointer: {
-                    assert(localidx & LOCAL_IS_WASM);
-                    WIL(NULL, WI_LOCAL_GET, localidx);
-                    emit_load_instruction(mod, &code, expr->type, 0);
-                    break;
-                }
-
-                default: assert(0);
-            }
-
-            break;
-        }
-
-        case Ast_Kind_Local: {
-            u64 tmp = bh_imap_get(&mod->local_map, (u64) expr);
-
-            if (tmp & LOCAL_IS_WASM) {
-                if (bh_arr_last(code).type == WI_LOCAL_SET && (u64) bh_arr_last(code).data.l == tmp) {
-                    bh_arr_last(code).type = WI_LOCAL_TEE;
-                } else {
-                    WIL(NULL, WI_LOCAL_GET, tmp);
-                }
-
-            } else {
-                u64 offset = 0;
-                emit_local_location(mod, &code, (AstLocal *) expr, &offset);
-
-                if (expr->type->kind != Type_Kind_Array) {
-                    emit_load_instruction(mod, &code, expr->type, offset);
-                } else if (offset != 0) {
-                    WID(NULL, WI_PTR_CONST, offset);
-                    WI(NULL, WI_PTR_ADD);
-                }
-            }
-
-            break;
-        }
-
-        case Ast_Kind_Global: {
-            i32 globalidx = (i32) bh_imap_get(&mod->index_map, (u64) expr);
-
-            WID(NULL, WI_GLOBAL_GET, globalidx);
-            break;
-        }
-
-        case Ast_Kind_NumLit: {
-            AstNumLit* lit = (AstNumLit *) expr;
-            WasmType lit_type = onyx_type_to_wasm_type(lit->type);
-            WasmInstruction instr = { WI_NOP, 0 };
-
-            if (lit_type == WASM_TYPE_INT32) {
-                instr.type = WI_I32_CONST;
-                instr.data.i1 = lit->value.i;
-            } else if (lit_type == WASM_TYPE_INT64) {
-                instr.type = WI_I64_CONST;
-                instr.data.l = lit->value.l;
-            } else if (lit_type == WASM_TYPE_FLOAT32) {
-                instr.type = WI_F32_CONST;
-                instr.data.f = lit->value.f;
-            } else if (lit_type == WASM_TYPE_FLOAT64) {
-                instr.type = WI_F64_CONST;
-                instr.data.d = lit->value.d;
-            }
-
-            WIR(NULL, instr);
-            break;
-        }
-
-        case Ast_Kind_StrLit: {
-            // :ProperLinking
-            AstStrLit *strlit = (AstStrLit *) expr;
-            assert(strlit->data_id > 0);
-            emit_data_relocation(mod, &code, strlit->data_id);
-
-            if (strlit->is_cstr == 0)
-                WID(NULL, WI_I32_CONST, strlit->length);
-            break;
-        }
-
-        case Ast_Kind_Struct_Literal: {
-            emit_struct_literal(mod, &code, (AstStructLiteral *) expr);
-            break;
-        }
-
-        case Ast_Kind_Array_Literal: {
-            emit_local_allocation(mod, &code, expr);
-            emit_array_literal(mod, &code, (AstArrayLiteral *) expr);
-            break;
-        }
-
-        case Ast_Kind_Range_Literal: {
-            emit_range_literal(mod, &code, (AstRangeLiteral *) expr);
-            break;
-        }
-
-        case Ast_Kind_Function: {
-            i32 elemidx = get_element_idx(mod, (AstFunction *) expr);
-
-            WID(NULL, WI_I32_CONST, elemidx);
-            break;
-        }
-
-        case Ast_Kind_Block:          emit_block(mod, &code, (AstBlock *) expr, 1); break;
-        case Ast_Kind_Do_Block:       emit_do_block(mod, &code, (AstDoBlock *) expr); break;
-        case Ast_Kind_Call:           emit_call(mod, &code, (AstCall *) expr); break;
-        case Ast_Kind_Argument:       emit_expression(mod, &code, ((AstArgument *) expr)->value); break;
-        case Ast_Kind_Intrinsic_Call: emit_intrinsic_call(mod, &code, (AstCall *) expr); break;
-        case Ast_Kind_Binary_Op:      emit_binop(mod, &code, (AstBinaryOp *) expr); break;
-        case Ast_Kind_Unary_Op:       emit_unaryop(mod, &code, (AstUnaryOp *) expr); break;
-        case Ast_Kind_Alias:          emit_expression(mod, &code, ((AstAlias *) expr)->alias); break;
-
-        case Ast_Kind_Address_Of: {
-            AstAddressOf* aof = (AstAddressOf *) expr;
-
-            if (node_is_addressable_literal((AstNode *) aof->expr)) {
-                aof->expr->flags |= Ast_Flag_Decl_Followed_By_Init;
-                aof->expr->flags |= Ast_Flag_Address_Taken;
-                emit_local_allocation(mod, &code, aof->expr);
-                emit_location(mod, &code, aof->expr);
-                emit_expression(mod, &code, aof->expr);
-                emit_store_instruction(mod, &code, aof->expr->type, 0);
-            }
-
-            emit_location(mod, &code, aof->expr);
-            break;
-        }
-
-        case Ast_Kind_Dereference: {
-            AstDereference* deref = (AstDereference *) expr;
-            emit_expression(mod, &code, deref->expr);
-            emit_load_instruction(mod, &code, deref->type, 0);
-            break;
-        }
-
-        case Ast_Kind_Subscript: {
-            AstSubscript* sub = (AstSubscript *) expr;
-            u64 offset = 0;
-            emit_subscript_location(mod, &code, sub, &offset);
-            emit_load_instruction(mod, &code, sub->type, offset);
-            break;
-        }
-
-        case Ast_Kind_Field_Access: {
-            AstFieldAccess* field = (AstFieldAccess* ) expr;
-
-            if (field->expr->kind == Ast_Kind_Param) {
-                if (type_get_param_pass(field->expr->type) == Param_Pass_By_Value && !type_is_pointer(field->expr->type)) {
-                    u64 localidx = bh_imap_get(&mod->local_map, (u64) field->expr) + field->idx;
-                    assert(localidx & LOCAL_IS_WASM);
-                    WIL(NULL, WI_LOCAL_GET, localidx);
-                    break;
-                }
-            }
-
-            if (is_lval((AstNode *) field->expr) || type_is_pointer(field->expr->type)) {
-                u64 offset = 0;
-                emit_field_access_location(mod, &code, field, &offset);
-                emit_load_instruction(mod, &code, field->type, offset);
-
-            } else {
-                emit_expression(mod, &code, field->expr);
-
-                i32 idx = type_get_idx_of_linear_member_with_offset(field->expr->type, field->offset);
-                i32 field_linear_members = type_linear_member_count(field->type);
-                i32 total_linear_members = type_linear_member_count(field->expr->type);
-
-                if (idx == 0) {
-                    // Easy case: the member is the first one and all other members just have to be dropped.
-                    fori (i, 0, total_linear_members - field_linear_members) WI(NULL, WI_DROP);
-
-                } else {
-                    // Tough case: Stack shuffling to make the member the only thing on the stack.
-                    // This is very similar to the compound_load/compound_store procedures but it is different enough
-                    // that I cannot find a good way to factor them all without just introducing a ton of complexity.
-                    fori (i, 0, total_linear_members - idx - field_linear_members) WI(NULL, WI_DROP);
-
-                    u64 *temporaries = bh_alloc_array(global_scratch_allocator, u64, field_linear_members);
-                    fori (i, 0, field_linear_members) temporaries[i] = 0;
-
-                    TypeWithOffset two = { 0 };
-                    forir (i, field_linear_members - 1, 0) {
-                        type_linear_member_lookup(field->type, i, &two);
-
-                        WasmType wt = onyx_type_to_wasm_type(two.type);
-                        temporaries[i] = local_raw_allocate(mod->local_alloc, wt);
-                        WIL(NULL, WI_LOCAL_SET, temporaries[i]);
-                    }
-
-                    fori (i, 0, idx) WI(NULL, WI_DROP);
-
-                    fori (i, 0, field_linear_members) {
-                        type_linear_member_lookup(field->type, i, &two);
-
-                        WIL(NULL, WI_LOCAL_GET, temporaries[i]);
-
-                        WasmType wt = onyx_type_to_wasm_type(two.type);
-                        local_raw_free(mod->local_alloc, wt);
-                    }
-
-                    bh_free(global_scratch_allocator, temporaries);
-                }
-            }
-
-            break;
-        }
-
-        case Ast_Kind_Slice: {
-            AstSubscript* sl = (AstSubscript *) expr;
-
-            emit_expression(mod, &code, sl->expr);
-
-            u64 lo_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
-            u64 hi_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
-
-            WI(NULL, WI_DROP);
-            WIL(NULL, WI_LOCAL_SET, hi_local);
-            WIL(NULL, WI_LOCAL_TEE, lo_local);
-            if (sl->elem_size != 1) {
-                WID(NULL, WI_I32_CONST, sl->elem_size);
-                WI(NULL, WI_I32_MUL);
-            }
-            emit_expression(mod, &code, sl->addr);
-            WI(NULL, WI_I32_ADD);
-            WIL(NULL, WI_LOCAL_GET, hi_local);
-            WIL(NULL, WI_LOCAL_GET, lo_local);
-            WI(NULL, WI_I32_SUB);
-
-            local_raw_free(mod->local_alloc, lo_local);
-            local_raw_free(mod->local_alloc, hi_local);
-            break;
-        }
-
-        case Ast_Kind_Size_Of: {
-            AstSizeOf* so = (AstSizeOf *) expr;
-            WID(NULL, WI_I32_CONST, so->size);
-            break;
-        }
-
-        case Ast_Kind_Align_Of: {
-            AstAlignOf* ao = (AstAlignOf *) expr;
-            WID(NULL, WI_I32_CONST, ao->alignment);
-            break;
-        }
-
-        case Ast_Kind_Enum_Value: {
-            AstEnumValue* ev = (AstEnumValue *) expr;
-            AstNumLit   * num = (AstNumLit *) ev->value;
-            assert(num->kind == Ast_Kind_NumLit);
-
-            WasmType backing_type = onyx_type_to_wasm_type(ev->type);
-            if      (backing_type == WASM_TYPE_INT32) WID(NULL, WI_I32_CONST, num->value.i);
-            else if (backing_type == WASM_TYPE_INT64) WID(NULL, WI_I64_CONST, num->value.l);
-            else onyx_report_error(ev->token->pos, Error_Critical, "Invalid backing type for enum.");
-            break;
-        }
-
-        case Ast_Kind_Memres: {
-            AstMemRes* memres = (AstMemRes *) expr;
-            emit_memory_reservation_location(mod, &code, memres);
-            emit_load_instruction(mod, &code, memres->type, 0);
-            break;
-        }
-
-        case Ast_Kind_File_Contents: {
-            AstFileContents* fc = (AstFileContents *) expr;
-
-            assert(fc->data_id > 0);
-            assert(fc->size > 0);
-
-            // :ProperLinking
-            emit_data_relocation(mod, &code, fc->data_id);
-            WID(NULL, WI_I32_CONST, fc->size);
-            break;
-        }
-
-        case Ast_Kind_Compound: {
-            AstCompound* compound = (AstCompound *) expr;
-
-            bh_arr_each(AstTyped *, expr, compound->exprs) {
-                emit_expression(mod, &code, *expr);
-            }
-            break;
-        }
-
-        case Ast_Kind_Call_Site: {
-            AstCallSite* callsite = (AstCallSite *) expr;
-
-            emit_expression(mod, &code, (AstTyped *) callsite->filename);
-            emit_expression(mod, &code, (AstTyped *) callsite->line);
-            emit_expression(mod, &code, (AstTyped *) callsite->column);
-            break;
-        }
-
-        case Ast_Kind_If_Expression: {
-            AstIfExpression* if_expr = (AstIfExpression *) expr;
-            emit_if_expression(mod, &code, if_expr);
-            break;
-        }
-
-        case Ast_Kind_Switch_Case: {
-            // This error message should be moved to checking, but this is the
-            // best place to do it right now.
-            onyx_report_error(expr->token->pos, Error_Critical, "'case' statements are only allowed in a 'switch' statement.");
-            break;
-        }
-
-        case Ast_Kind_Code_Block: {
-            // Like above, this error message should be moved to checking, but
-            // this is the best place to do it right now.
-            onyx_report_error(expr->token->pos, Error_Critical, "'#quote' blocks are only to be used at compile-time. Using them as a runtime value is not allowed.");
-            break;
-        }
-
-        case Ast_Kind_Foreign_Block: {
-            AstForeignBlock *fb = (AstForeignBlock *) expr;
-            WID(NULL, WI_I32_CONST, fb->foreign_block_number);
-            break;
-        }
-
-        case Ast_Kind_Zero_Value: {
-            AstZeroValue *zv = (AstZeroValue *) expr;
-            assert(zv->type);
-            emit_zero_value_for_type(mod, &code, zv->type, zv->token);
-            break;
-        }
-
-        default:
-            bh_printf("Unhandled case: %d\n", expr->kind);
-            DEBUG_HERE;
-            assert(0);
-    }
-
-    if ((expr->flags & Ast_Flag_Expr_Ignored) != 0 && !type_results_in_void(expr->type)) {
-        i32 mem_count = 1;
-        if (type_is_compound(expr->type)) mem_count = type_linear_member_count(expr->type);
-        fori (i, 0, mem_count) WI(NULL, WI_DROP);
-    }
-
-    *pcode = code;
-}
-
-static const WasmInstructionType cast_map[][12] = {
-    //          I8               U8                  I16                 U16                I32                 U32                I64                U64                F32                F64                PTR
-    /* I8  */ { WI_NOP,          WI_NOP,             WI_I32_EXTEND_8_S,  WI_NOP,            WI_I32_EXTEND_8_S,  WI_NOP,            WI_I64_FROM_I32_S, WI_I64_FROM_I32_S, WI_UNREACHABLE,    WI_UNREACHABLE,    WI_UNREACHABLE,  WI_UNREACHABLE },
-    /* U8  */ { WI_NOP,          WI_NOP,             WI_NOP,             WI_NOP,            WI_NOP,             WI_NOP,            WI_I64_FROM_I32_U, WI_I64_FROM_I32_U, WI_UNREACHABLE,    WI_UNREACHABLE,    WI_UNREACHABLE,  WI_UNREACHABLE },
-    /* I16 */ { WI_NOP,          WI_NOP,             WI_NOP,             WI_NOP,            WI_I32_EXTEND_16_S, WI_NOP,            WI_I64_FROM_I32_S, WI_I64_FROM_I32_S, WI_UNREACHABLE,    WI_UNREACHABLE,    WI_UNREACHABLE,  WI_UNREACHABLE },
-    /* U16 */ { WI_NOP,          WI_NOP,             WI_NOP,             WI_NOP,            WI_NOP,             WI_NOP,            WI_I64_FROM_I32_U, WI_I64_FROM_I32_U, WI_UNREACHABLE,    WI_UNREACHABLE,    WI_UNREACHABLE,  WI_UNREACHABLE },
-    /* I32 */ { WI_NOP,          WI_NOP,             WI_NOP,             WI_NOP,            WI_NOP,             WI_NOP,            WI_I64_FROM_I32_S, WI_I64_FROM_I32_S, WI_F32_FROM_I32_S, WI_F64_FROM_I32_S, WI_NOP,          WI_NOP         },
-    /* U32 */ { WI_NOP,          WI_NOP,             WI_NOP,             WI_NOP,            WI_NOP,             WI_NOP,            WI_I64_FROM_I32_U, WI_I64_FROM_I32_U, WI_F32_FROM_I32_U, WI_F64_FROM_I32_U, WI_NOP,          WI_NOP         },
-    /* I64 */ { WI_I32_FROM_I64, WI_I32_FROM_I64,    WI_I32_FROM_I64,    WI_I32_FROM_I64,   WI_I32_FROM_I64,    WI_I32_FROM_I64,   WI_NOP,            WI_NOP,            WI_F32_FROM_I64_S, WI_F64_FROM_I64_S, WI_I32_FROM_I64, WI_UNREACHABLE },
-    /* U64 */ { WI_I32_FROM_I64, WI_I32_FROM_I64,    WI_I32_FROM_I64,    WI_I32_FROM_I64,   WI_I32_FROM_I64,    WI_I32_FROM_I64,   WI_NOP,            WI_NOP,            WI_F32_FROM_I64_U, WI_F64_FROM_I64_U, WI_I32_FROM_I64, WI_UNREACHABLE },
-    /* F32 */ { WI_UNREACHABLE,  WI_UNREACHABLE,     WI_UNREACHABLE,     WI_UNREACHABLE,    WI_I32_FROM_F32_S,  WI_I32_FROM_F32_U, WI_I64_FROM_F32_S, WI_I64_FROM_F32_U, WI_NOP,            WI_F64_FROM_F32,   WI_UNREACHABLE,  WI_UNREACHABLE },
-    /* F64 */ { WI_UNREACHABLE,  WI_UNREACHABLE,     WI_UNREACHABLE,     WI_UNREACHABLE,    WI_I32_FROM_F64_S,  WI_I32_FROM_F64_U, WI_I64_FROM_F64_S, WI_I64_FROM_F64_U, WI_F32_FROM_F64,   WI_NOP,            WI_UNREACHABLE,  WI_UNREACHABLE },
-    /* PTR */ { WI_UNREACHABLE,  WI_UNREACHABLE,     WI_UNREACHABLE,     WI_UNREACHABLE,    WI_NOP,             WI_NOP,            WI_I64_FROM_I32_U, WI_I64_FROM_I32_U, WI_UNREACHABLE,    WI_UNREACHABLE,    WI_NOP,          WI_UNREACHABLE },
-    /* TYP */ { WI_UNREACHABLE,  WI_UNREACHABLE,     WI_UNREACHABLE,     WI_UNREACHABLE,    WI_NOP,             WI_NOP,            WI_UNREACHABLE,    WI_UNREACHABLE,    WI_UNREACHABLE,    WI_UNREACHABLE,    WI_UNREACHABLE,  WI_NOP         },
-};
-
-EMIT_FUNC(cast, AstUnaryOp* cast) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    emit_expression(mod, &code, cast->expr);
-
-    Type* from = cast->expr->type;
-    Type* to = cast->type;
-    if (from->kind == Type_Kind_Enum) from = from->Enum.backing;
-    if (to->kind == Type_Kind_Enum) to = to->Enum.backing;
-
-    if (type_is_simd(from) && type_is_simd(to)) {
-        *pcode = code;
-        return;
-    }
-
-    if (to->kind == Type_Kind_Basic && to->Basic.kind == Basic_Kind_Void) {
-        WI(NULL, WI_DROP);
-        *pcode = code;
-        return;
-    }
-
-    if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_Array) {
-        WID(NULL, WI_I32_CONST, from->Array.count);
-        *pcode = code;
-        return;
-    }
-
-    if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_DynArray) {
-        WI(NULL, WI_DROP);
-        WI(NULL, WI_DROP);
-        WI(NULL, WI_DROP);
-        *pcode = code;
-        return;
-    }
-
-    if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_Slice) {
-        // Nothing needs to be done because they are identical
-        *pcode = code;
-        return;
-    }
-
-    if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_VarArgs) {
-        // Nothing needs to be done because they are identical
-        *pcode = code;
-        return;
-    }
-
-    if (to->kind == Type_Kind_Distinct || from->kind == Type_Kind_Distinct) {
-        // Nothing needs to be done because they are identical
-        *pcode = code;
-        return;
-    }
-
-    i32 fromidx = -1, toidx = -1;
-    if (from->Basic.flags & Basic_Flag_Pointer || from->kind == Type_Kind_Array) {
-        fromidx = 10;
-    }
-    else if (from->Basic.flags & Basic_Flag_Integer) {
-        b32 unsign = (from->Basic.flags & Basic_Flag_Unsigned) != 0;
-
-        fromidx = log2_dumb(from->Basic.size) * 2 + unsign;
-    }
-    else if (from->Basic.flags & Basic_Flag_Float) {
-        if      (from->Basic.size == 4) fromidx = 8;
-        else if (from->Basic.size == 8) fromidx = 9;
-    }
-    else if (from->Basic.flags & Basic_Flag_Boolean) {
-        fromidx = 0;
-    }
-    else if (from->Basic.flags & Basic_Flag_Type_Index) {
-        fromidx = 11;
-    }
-
-    if (to->Basic.flags & Basic_Flag_Pointer || to->kind == Type_Kind_Array) {
-        toidx = 10;
-    }
-    else if (to->Basic.flags & Basic_Flag_Integer) {
-        b32 unsign = (to->Basic.flags & Basic_Flag_Unsigned) != 0;
-
-        toidx = log2_dumb(to->Basic.size) * 2 + unsign;
-    }
-    else if (to->Basic.flags & Basic_Flag_Float) {
-        if      (to->Basic.size == 4) toidx = 8;
-        else if (to->Basic.size == 8) toidx = 9;
-    }
-    else if (to->Basic.flags & Basic_Flag_Boolean) {
-        toidx = 0;
-    }
-    else if (to->Basic.flags & Basic_Flag_Type_Index) {
-        toidx = 11;
-    }
-
-    if (fromidx != -1 && toidx != -1) {
-        WasmInstructionType cast_op = cast_map[fromidx][toidx];
-        assert(cast_op != WI_UNREACHABLE);
-
-        if (cast_op != WI_NOP) {
-            WI(NULL, cast_op);
-        }
-    }
-
-    *pcode = code;
-}
-
-EMIT_FUNC(return, AstReturn* ret) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    // If we have an expression to return, we see if it should be placed on the linear memory stack, or the WASM stack.
-    if (ret->expr) {
-        if (bh_arr_length(mod->return_location_stack) > 0) {
-            AstLocal* dest = bh_arr_last(mod->return_location_stack);
-            u64 dest_loc = bh_imap_get(&mod->local_map, (u64) dest);
-            b32 dest_is_local = (b32) ((dest_loc & LOCAL_IS_WASM) != 0);
-
-            u64 offset = 0;
-            if (!dest_is_local) emit_local_location(mod, &code, dest, &offset);
-
-            emit_expression(mod, &code, ret->expr);
-
-            if (!dest_is_local) emit_store_instruction(mod, &code, dest->type, offset);
-            else                WIL(NULL, WI_LOCAL_SET, dest_loc);
-
-        } else if (mod->curr_cc == CC_Return_Stack) {
-            WIL(NULL, WI_LOCAL_GET, mod->stack_base_idx);
-            WID(NULL, WI_I32_CONST, type_size_of(ret->expr->type));
-            WI(NULL, WI_I32_SUB);
-
-            emit_expression(mod, &code, ret->expr);
-            emit_store_instruction(mod, &code, ret->expr->type, 0);
-
-        } else {
-            emit_expression(mod, &code, ret->expr);
-        }
-    }
-
-    // Clear the normal deferred statements
-    emit_deferred_stmts(mod, &code);
-
-    i64 jump_label = get_structured_jump_label(mod, Jump_Type_Return, 1);
-    if (jump_label >= 0) {
-        WIL(NULL, WI_JUMP, jump_label);
-
-    } else {
-        // Clear the rest of the deferred statements
-        if (bh_arr_length(mod->deferred_stmts) > 0) {
-            i32 i = bh_arr_length(mod->deferred_stmts) - 1;
-            while (i >= 0) {
-                emit_deferred_stmt(mod, &code, mod->deferred_stmts[i]);
-                i--;
-            }
-        }
-
-        // Make a patch for the two instructions needed to restore the stack pointer
-        SUBMIT_PATCH(mod->stack_leave_patches, 0);
-        WI(NULL, WI_NOP);
-        WI(NULL, WI_NOP);
-
-        WI(NULL, WI_RETURN);
-    }
-
-    *pcode = code;
-}
-
-EMIT_FUNC(stack_enter, u64 stacksize) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    bh_align(stacksize, 16);
-
-    u64 stack_top_idx = bh_imap_get(&mod->index_map, (u64) &builtin_stack_top);
-
-    // HACK: slightly... There will be space for 5 instructions
-    if (stacksize == 0) {
-        code[0] = (WasmInstruction) { WI_GLOBAL_GET, { .l = stack_top_idx } };
-        code[1] = (WasmInstruction) { WI_LOCAL_SET,  { .l = mod->stack_base_idx} };
-        code[2] = (WasmInstruction) { WI_NOP,        0 };
-        code[3] = (WasmInstruction) { WI_NOP,        0 };
-        code[4] = (WasmInstruction) { WI_NOP,        0 };
-    } else {
-        code[0] = (WasmInstruction) { WI_GLOBAL_GET, { .l = stack_top_idx } };
-        code[1] = (WasmInstruction) { WI_LOCAL_TEE,  { .l = mod->stack_base_idx} };
-        code[2] = (WasmInstruction) { WI_I32_CONST,  { .l = stacksize } };
-        code[3] = (WasmInstruction) { WI_I32_ADD,    0 };
-        code[4] = (WasmInstruction) { WI_GLOBAL_SET, { .l = stack_top_idx } };
-    }
-
-    *pcode = code;
-}
-
-EMIT_FUNC(zero_value, WasmType wt) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    switch (wt) {
-        case WASM_TYPE_INT32:   WIL(NULL, WI_I32_CONST, 0); break;
-        case WASM_TYPE_INT64:   WIL(NULL, WI_I64_CONST, 0); break;
-        case WASM_TYPE_FLOAT32: WIL(NULL, WI_F32_CONST, 0); break;
-        case WASM_TYPE_FLOAT64: WIL(NULL, WI_F64_CONST, 0); break;
-        case WASM_TYPE_VAR128:  {
-            static u8 zero_v128[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
-            WIP(NULL, WI_V128_CONST, &zero_v128);
-            break;
-        }
-    }
-
-    *pcode = code;
-}
-
-EMIT_FUNC(zero_value_for_type, Type* type, OnyxToken* where) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    if (type_is_structlike_strict(type)) {
-        i32 mem_count = type_linear_member_count(type);
-        TypeWithOffset two;
-
-        fori (i, 0, mem_count) {
-            type_linear_member_lookup(type, i, &two);
-            emit_zero_value_for_type(mod, &code, two.type, where);
-        }
-    }
-    else if (type->kind == Type_Kind_Function) {
-        WID(NULL, WI_I32_CONST, mod->null_proc_func_idx);
-    }
-    else {
-        WasmType wt = onyx_type_to_wasm_type(type);
-        if (wt == WASM_TYPE_VOID) {
-            onyx_report_error(where->pos, Error_Critical, "Cannot produce a zero-value for this type.");
-        }
-        emit_zero_value(mod, &code, wt);
-    }
-
-    *pcode = code;
-}
-
-static i32 generate_type_idx(OnyxWasmModule* mod, Type* ft) {
-    if (ft->kind != Type_Kind_Function) return -1;
-
-    static char type_repr_buf[128];
-    char* t = type_repr_buf;
-
-    Type** param_type = ft->Function.params;
-    i32 param_count = ft->Function.param_count;
-    i32 params_left = param_count;
-
-    while (params_left-- > 0) {
-        if (type_get_param_pass(*param_type) == Param_Pass_By_Implicit_Pointer) {
-            *(t++) = (char) onyx_type_to_wasm_type(&basic_types[Basic_Kind_Rawptr]);
-
-        }
-        else if (type_is_structlike_strict(*param_type)) {
-            u32 mem_count = type_structlike_mem_count(*param_type);
-            StructMember smem;
-
-            fori (i, 0, mem_count) {
-                type_lookup_member_by_idx(*param_type, i, &smem);
-                *(t++) = (char) onyx_type_to_wasm_type(smem.type);
-            }
-
-            param_count += mem_count - 1;
-
-        } else {
-            *(t++) = (char) onyx_type_to_wasm_type(*param_type);
-        }
-
-        param_type++;
-    }
-    *(t++) = ':';
-
-    // HACK: Slightly: the wasm type for structs has to be 0x00
-    WasmType return_type = onyx_type_to_wasm_type(ft->Function.return_type);
-    if (ft->Function.return_type->kind == Type_Kind_Struct && !type_is_compound(ft->Function.return_type)) {
-        return_type = onyx_type_to_wasm_type(ft->Function.return_type->Struct.linear_members[0].type);
-    }
-
-    *(t++) = (char) return_type;
-    *t = '\0';
-
-    i32 type_idx = 0;
-    i32 index = shgeti(mod->type_map, type_repr_buf);
-    if (index != -1) {
-        type_idx = mod->type_map[index].value;
-    } else {
-        // NOTE: Make a new type
-        WasmFuncType* type = (WasmFuncType*) bh_alloc(mod->allocator, sizeof(WasmFuncType) + sizeof(WasmType) * param_count);
-        type->return_type = return_type;
-        type->param_count = param_count;
-
-        fori (i, 0, type->param_count) {
-            type->param_types[i] = type_repr_buf[i];
-        }
-
-        bh_arr_push(mod->types, type);
-
-        shput(mod->type_map, type_repr_buf, mod->next_type_idx);
-        type_idx = mod->next_type_idx;
-        mod->next_type_idx++;
-    }
-
-    return type_idx;
-}
-
-static i32 get_element_idx(OnyxWasmModule* mod, AstFunction* func) {
-    if (bh_imap_has(&mod->elem_map, (u64) func)) {
-        return bh_imap_get(&mod->elem_map, (u64) func);
-    } else {
-        i32 idx = mod->next_elem_idx;
-        bh_imap_put(&mod->elem_map, (u64) func, idx);
-
-        i32 func_idx = bh_imap_get(&mod->index_map, (u64) func);
-        bh_arr_push(mod->elems, func_idx);
-
-        mod->next_elem_idx++;
-
-        return idx;
-    }
-}
-
-static void emit_function(OnyxWasmModule* mod, AstFunction* fd) {
-    if (!should_emit_function(fd)) return;
-
-    i32 type_idx = generate_type_idx(mod, fd->type);
-
-    WasmFunc wasm_func = { 0 };
-    wasm_func.type_idx = type_idx;
-    wasm_func.location = fd->token;
-
-    bh_arr_new(mod->allocator, wasm_func.code, 16);
-
-    i32 func_idx = (i32) bh_imap_get(&mod->index_map, (u64) fd);
-    mod->current_func_idx = func_idx;
-
-    debug_begin_function(mod, func_idx, fd->token, get_function_name(fd));
-
-    if (fd == builtin_initialize_data_segments && context.options->use_post_mvp_features) {
-        emit_initialize_data_segments_body(mod, &wasm_func.code);
-
-        debug_emit_instruction(mod, NULL);
-        bh_arr_push(wasm_func.code, ((WasmInstruction){ WI_BLOCK_END, 0x00 }));
-
-        bh_arr_set_at(mod->funcs, func_idx - mod->foreign_function_count, wasm_func);
-        mod->current_func_idx = -1;
-
-        debug_end_function(mod);
-        return;
-    }
-
-    if (fd == builtin_run_init_procedures) {
-        emit_run_init_procedures(mod, &wasm_func.code);
-
-        debug_emit_instruction(mod, NULL);
-        bh_arr_push(wasm_func.code, ((WasmInstruction){ WI_BLOCK_END, 0x00 }));
-
-        bh_arr_set_at(mod->funcs, func_idx - mod->foreign_function_count, wasm_func);
-        mod->current_func_idx = -1;
-
-        debug_end_function(mod);
-        return;
-    }
-
-    if (fd->body != NULL) {
-        mod->local_alloc = &wasm_func.locals;
-
-        // NOTE: Generate the local map
-        u64 localidx = 0;
-        bh_arr_each(AstParam, param, fd->params) {
-            switch (type_get_param_pass(param->local->type)) {
-                case Param_Pass_By_Value: {
-                    if (type_is_structlike_strict(param->local->type)) {
-                        debug_introduce_symbol(mod, param->local->token, DSL_REGISTER, localidx | LOCAL_IS_WASM, param->local->type);
-                        bh_imap_put(&mod->local_map, (u64) param->local, localidx | LOCAL_IS_WASM);
-                        localidx += type_structlike_mem_count(param->local->type);
-
-                        break;
-                    }
-                    // fallthrough
-                }
-
-                case Param_Pass_By_Implicit_Pointer: {
-                    debug_introduce_symbol(mod, param->local->token, DSL_REGISTER, localidx | LOCAL_IS_WASM, param->local->type);
-                    bh_imap_put(&mod->local_map, (u64) param->local, localidx++ | LOCAL_IS_WASM);
-                    break;
-                }
-
-                default: assert(0);
-            }
-        }
-
-        mod->local_alloc->param_count = localidx;
-
-        mod->curr_cc = type_function_get_cc(fd->type);
-        assert(mod->curr_cc != CC_Undefined);
-
-        bh_arr_clear(mod->stack_leave_patches);
-
-        debug_emit_instruction(mod, fd->token);
-        debug_emit_instruction(mod, fd->token);
-        debug_emit_instruction(mod, fd->token);
-        debug_emit_instruction(mod, fd->token);
-        debug_emit_instruction(mod, fd->token);
-        bh_arr_insert_end(wasm_func.code, 5);
-        fori (i, 0, 5) wasm_func.code[i] = (WasmInstruction) { WI_NOP, 0 };
-
-        // TODO: Emit debug info for the above instructions
-
-        mod->stack_base_idx = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
-
-        // Generate code
-        emit_function_body(mod, &wasm_func.code, fd);
-
-        if (mod->local_alloc->max_stack > 0 || mod->curr_cc == CC_Return_Stack) {
-            emit_stack_enter(mod, &wasm_func.code, mod->local_alloc->max_stack);
-
-            // Place all stack leaves in patch locations. These will (probably) all be
-            // right before a "return" instruction.
-            debug_emit_instruction(mod, NULL);
-            debug_emit_instruction(mod, NULL);
-
-            u64 stack_top_idx = bh_imap_get(&mod->index_map, (u64) &builtin_stack_top);
-            bh_arr_push(wasm_func.code, ((WasmInstruction) { WI_LOCAL_GET,  { .l = mod->stack_base_idx } }));
-            bh_arr_push(wasm_func.code, ((WasmInstruction) { WI_GLOBAL_SET, { .l = stack_top_idx } }));
-
-            bh_arr_each(PatchInfo, patch, mod->stack_leave_patches) {
-                wasm_func.code[patch->instruction_index + 0] = (WasmInstruction) { WI_LOCAL_GET,  { .l = mod->stack_base_idx } };
-                wasm_func.code[patch->instruction_index + 1] = (WasmInstruction) { WI_GLOBAL_SET, { .l = stack_top_idx } };
-            }
-        }
-    }
-
-    WasmFuncType* ft = mod->types[type_idx];
-    emit_zero_value(mod, &wasm_func.code, ft->return_type);
-
-    if (fd->closing_brace) {
-        debug_emit_instruction(mod, fd->closing_brace);
-        bh_arr_push(wasm_func.code, ((WasmInstruction){ WI_NOP, 0x00 }));
-    }
-
-    debug_emit_instruction(mod, NULL);
-    bh_arr_push(wasm_func.code, ((WasmInstruction){ WI_BLOCK_END, 0x00 }));
-
-    bh_imap_clear(&mod->local_map);
-
-    bh_arr_set_at(mod->funcs, func_idx - mod->foreign_function_count, wasm_func);
-    mod->current_func_idx = -1;
-
-    debug_end_function(mod);
-}
-
-static void emit_foreign_function(OnyxWasmModule* mod, AstFunction* fd) {
-    if (!should_emit_function(fd)) return;
-
-    i32 type_idx = generate_type_idx(mod, fd->type);
-
-    WasmImport import = {
-        .kind = WASM_FOREIGN_FUNCTION,
-        .idx  = type_idx,
-        .mod  = bh_aprintf(global_heap_allocator, "%b", fd->foreign_module->text, fd->foreign_module->length),
-        .name = bh_aprintf(global_heap_allocator, "%b", fd->foreign_name->text, fd->foreign_name->length),
-    };
-
-    bh_arr_push(mod->imports, import);
-    return;
-}
-
-static void emit_export_directive(OnyxWasmModule* mod, AstDirectiveExport* export) {
-    assert(export->export_name);
-    assert(export->export);
-
-    token_toggle_end(export->export_name);
-
-    i64 idx = bh_imap_get(&mod->index_map, (u64) export->export);
-
-    WasmExport wasm_export;
-    wasm_export.idx = (i32) idx;
-
-    switch (export->export->kind) {
-        case Ast_Kind_Function: wasm_export.kind = WASM_FOREIGN_FUNCTION;
-                                break;
-
-        case Ast_Kind_Global:   wasm_export.kind = WASM_FOREIGN_GLOBAL;
-                                break;
-    }
-
-    shput(mod->exports, export->export_name->text, wasm_export);
-    mod->export_count++;
-
-    token_toggle_end(export->export_name);
-    return;
-}
-
-static void emit_global(OnyxWasmModule* module, AstGlobal* global) {
-    WasmType global_type = onyx_type_to_wasm_type(global->type);
-
-    WasmGlobal glob = {
-        .type = global_type,
-        .mutable = (global->flags & Ast_Flag_Const) == 0,
-        .initial_value = NULL,
-    };
-
-    i32 global_idx = (i32) bh_imap_get(&module->index_map, (u64) global);
-
-    bh_arr_new(global_heap_allocator, glob.initial_value, 1);
-
-    switch (global_type) {
-        case WASM_TYPE_INT32:   bh_arr_push(glob.initial_value, ((WasmInstruction) { WI_I32_CONST, 0 })); break;
-        case WASM_TYPE_INT64:   bh_arr_push(glob.initial_value, ((WasmInstruction) { WI_I64_CONST, 0 })); break;
-        case WASM_TYPE_FLOAT32: bh_arr_push(glob.initial_value, ((WasmInstruction) { WI_F32_CONST, 0 })); break;
-        case WASM_TYPE_FLOAT64: bh_arr_push(glob.initial_value, ((WasmInstruction) { WI_F64_CONST, 0 })); break;
-
-        default: assert(("Invalid global type", 0)); break;
-    }
-
-    bh_arr_set_at(module->globals, global_idx, glob);
-
-    if (global == &builtin_stack_top)
-        module->stack_top_ptr = &module->globals[global_idx].initial_value[0].data.i1;
-
-    if (global == &builtin_heap_start)
-        module->heap_start_ptr = &module->globals[global_idx].initial_value[0].data.i1;
-
-    if (global == &builtin_tls_size)
-        module->globals[global_idx].initial_value[0].data.i1 =  module->next_tls_offset;
-}
-
-static void emit_string_literal(OnyxWasmModule* mod, AstStrLit* strlit) {
-
-    // NOTE: Allocating more than necessary, but there are no cases
-    // in a string literal that create more bytes than already
-    // existed. You can create less however ('\n' => 0x0a).
-    i8* strdata = bh_alloc_array(global_heap_allocator, i8, strlit->token->length + 1);
-    i32 length  = string_process_escape_seqs(strdata, strlit->token->text, strlit->token->length);
-
-    i32 index = shgeti(mod->string_literals, (char *) strdata);
-    if (index != -1) {
-        StrLitInfo sti = mod->string_literals[index].value;
-        strlit->data_id = sti.data_id;
-        strlit->length  = sti.len;
-
-        bh_free(global_heap_allocator, strdata);
-        return;
-    }
-    
-    // :ProperLinking
-    u32 actual_length = length + (strlit->is_cstr ? 1 : 0);
-    WasmDatum datum = {
-        .alignment = 1,
-        .length = actual_length,
-        .data = strdata,
-    };
-
-    strlit->data_id = emit_data_entry(mod, &datum);
-    strlit->length  = length;
-
-    // :ProperLinking
-    shput(mod->string_literals, (char *) strdata, ((StrLitInfo) { strlit->data_id, strlit->length }));
-}
-
-static u32 emit_data_entry(OnyxWasmModule *mod, WasmDatum *datum) {
-    datum->offset_ = 0;
-    datum->id = NEXT_DATA_ID(mod); 
-    bh_arr_push(mod->data, *datum);
-    return datum->id;
-}
-
-static void emit_constexpr(ConstExprContext *ctx, AstTyped *node, u32 offset) {
-    if (!emit_constexpr_(ctx, node, offset)) {
-        onyx_report_error(node->token->pos, Error_Critical,
-            "Cannot generate constant data for '%s'.",
-            onyx_ast_node_kind_string(node->kind));
-    }
-}
-
-static b32 emit_constexpr_(ConstExprContext *ctx, AstTyped *node, u32 offset) {
-#define CE(type, off) (*((type *) bh_pointer_add(ctx->data, offset + (off))))
-    assert(ctx->data_id);
-    assert(ctx->data);
-
-    b32 retval = 1;
-    node = (AstTyped *) strip_aliases((AstNode *) node);
-
-    if (node_is_type((AstNode *) node)) {
-        Type* constructed_type = type_build_from_ast(context.ast_alloc, (AstType *) node);
-        CE(i32, 0) = constructed_type->id;
-        return 1;
-    }
-
-    switch (node->kind) {
-    case Ast_Kind_Array_Literal: {
-        AstArrayLiteral* al = (AstArrayLiteral *) node;
-
-        i32 i = 0;
-        i32 elem_size = type_size_of(al->type->Array.elem);
-
-        bh_arr_each(AstTyped *, expr, al->values) {
-            retval &= emit_constexpr_(ctx, *expr, i * elem_size + offset);
-            i++;
-        }
-
-        break;
-    }
-
-    case Ast_Kind_Struct_Literal: {
-        AstStructLiteral* sl = (AstStructLiteral *) node;
-
-        Type* sl_type = sl->type;
-        // ROBUSTNESS: Handle cases for slices and dynamic arrays
-        if (sl_type->kind != Type_Kind_Struct && sl_type->kind != Type_Kind_Slice && sl_type->kind != Type_Kind_DynArray) {
-            retval = 0;
-            break;
-        }
-
-        i32 mem_count = type_structlike_mem_count(sl_type);
-        StructMember smem;
-
-        fori (i, 0, mem_count) {
-            type_lookup_member_by_idx(sl_type, i, &smem);
-            retval &= emit_constexpr_(ctx, sl->args.values[i], smem.offset + offset);
-        }
-
-        break;
-    }
-
-    case Ast_Kind_StrLit: {
-        AstStrLit* sl = (AstStrLit *) node;
-
-        // NOTE: This assumes the data_id and the length fields have been filled out
-        // by emit_string_literal.
-        if (POINTER_SIZE == 4) {
-            CE(u32, 0) = 0;
-            CE(u32, 4) = sl->length;
-        } else {
-            CE(u64, 0) = 0;
-            CE(u64, 8) = sl->length;
-        }
-
-        assert(sl->data_id > 0);
-
-        DatumPatchInfo patch;
-        patch.kind = Datum_Patch_Data;
-        patch.index = ctx->data_id;
-        patch.location = offset;
-        patch.data_id = sl->data_id;
-        patch.offset = 0;
-        bh_arr_push(ctx->module->data_patches, patch);
-
-        break;
-    }
-
-    case Ast_Kind_Enum_Value: {
-        AstEnumValue* ev = (AstEnumValue *) node;
-        retval &= emit_constexpr_(ctx, (AstTyped *) ev->value, offset);
-        break;
-    }
-
-    case Ast_Kind_Function: {
-        AstFunction* func = (AstFunction *) node;
-        CE(u32, 0) = get_element_idx(ctx->module, func);
-        break;
-    }
-
-    case Ast_Kind_Size_Of: {
-        AstSizeOf* so = (AstSizeOf *) node;
-        CE(u32, 0) = so->size;
-        break;
-    }
-
-    case Ast_Kind_Align_Of: {
-        AstAlignOf* ao = (AstAlignOf *) node;
-        CE(u32, 0) = ao->alignment;
-        break;
-    }
-
-    case Ast_Kind_Zero_Value: {
-        memset(bh_pointer_add(ctx->data, offset), 0, type_size_of(node->type));
-        break;
-    }
-
-    case Ast_Kind_NumLit: {
-        // NOTE: This makes a big assumption that we are running on a
-        // little endian machine, since WebAssembly is little endian
-        // by specification. This is probably a safe assumption, but
-        // should probably be fixed at some point.
-        //                                - brendanfh  2020/12/15
-
-        Type* effective_type = node->type;
-
-        if (effective_type->kind == Type_Kind_Enum)
-            effective_type = effective_type->Enum.backing;
-
-        switch (effective_type->Basic.kind) {
-        case Basic_Kind_Bool:
-        case Basic_Kind_I8:
-        case Basic_Kind_U8:
-            CE(i8, 0) = (i8) ((AstNumLit *) node)->value.i;
-            return retval;
-
-        case Basic_Kind_I16:
-        case Basic_Kind_U16:
-            CE(i16, 0) = (i16) ((AstNumLit *) node)->value.i;
-            return retval;
-
-        case Basic_Kind_I32:
-        case Basic_Kind_U32:
-        case Basic_Kind_Rawptr:
-            CE(i32, 0) = ((AstNumLit *) node)->value.i;
-            return retval;
-
-        case Basic_Kind_I64:
-        case Basic_Kind_U64:
-            CE(i64, 0) = ((AstNumLit *) node)->value.l;
-            return retval;
-
-        case Basic_Kind_F32:
-            CE(f32, 0) = ((AstNumLit *) node)->value.f;
-            return retval;
-
-        case Basic_Kind_F64:
-            CE(f64, 0) = ((AstNumLit *) node)->value.d;
-            return retval;
-
-        default: break;
-        }
-
-        //fallthrough
-    }
-
-    case Ast_Kind_Code_Block: break;
-
-    default: retval = 0;
-    }
-
-    return retval;
-
-#undef CE
-}
-
-static void emit_memory_reservation(OnyxWasmModule* mod, AstMemRes* memres) {
-    // :ProperLinking
-    Type* effective_type = memres->type;
-
-    u64 alignment = type_alignment_of(effective_type);
-    u64 size = type_size_of(effective_type);
-
-    if (type_table_node != NULL && (AstMemRes *) type_table_node == memres) {
-        u64 table_location = build_type_table(mod);
-        memres->data_id = table_location;
-        return;
-    }
-
-    if (foreign_blocks_node != NULL && (AstMemRes *) foreign_blocks_node == memres) {
-        u64 foreign_blocks_location = build_foreign_blocks(mod);
-        memres->data_id = foreign_blocks_location;
-        return;
-    }
-
-    if (tagged_procedures_node != NULL && (AstMemRes *) tagged_procedures_node == memres) {
-        u64 tagged_procedures_location = build_tagged_procedures(mod);
-        memres->data_id = tagged_procedures_location;
-        return;
-    }
-
-    if (memres->threadlocal) {
-        memres->tls_offset = mod->next_tls_offset;
-        bh_align(memres->tls_offset, alignment);
-        mod->next_tls_offset = memres->tls_offset + size;
-
-    } else {
-        // :ProperLinking
-        u8* data = NULL;
-        if (memres->initial_value != NULL) {
-            assert(!memres->threadlocal);
-            data = bh_alloc(global_heap_allocator, size);
-        }
-
-        WasmDatum datum = {
-            .alignment = alignment,
-            .length = size,
-            .data = data,
-        };
-        memres->data_id = emit_data_entry(mod, &datum);
-
-        if (memres->initial_value != NULL) {
-            ConstExprContext constexpr_ctx;
-            constexpr_ctx.module = mod;
-            constexpr_ctx.data = data;
-            constexpr_ctx.data_id = memres->data_id;
-            emit_constexpr(&constexpr_ctx, memres->initial_value, 0);
-        }
-    }
-}
-
-static void emit_file_contents(OnyxWasmModule* mod, AstFileContents* fc) {
-    // INVESTIGATE: I think that filename should always be NULL when this function starts because
-    // a file contents entity is only processed once and therefore should only go through this step
-    // once. But somehow filename isn't NULL occasionally so I have to check for that...
-    //                                                                      - brendanfh  2021/05/23
-    if (fc->filename == NULL) {
-        const char* parent_file = fc->token->pos.filename;
-        if (parent_file == NULL) parent_file = ".";
-
-        char* parent_folder = bh_path_get_parent(parent_file, global_scratch_allocator);
-        
-        OnyxToken *filename_token = fc->filename_expr->token;
-
-        token_toggle_end(filename_token);
-        char* temp_fn     = bh_alloc_array(global_scratch_allocator, char, filename_token->length);
-        i32   temp_fn_len = string_process_escape_seqs(temp_fn, filename_token->text, filename_token->length);
-        char* filename    = bh_lookup_file(temp_fn, parent_folder, "", 0, NULL, 0);
-        fc->filename      = bh_strdup(global_heap_allocator, filename);
-        token_toggle_end(filename_token);
-    }
-
-    i32 index = shgeti(mod->loaded_file_info, fc->filename);
-    if (index != -1) {
-        StrLitInfo info = mod->loaded_file_info[index].value;
-        fc->data_id = info.data_id;
-        fc->size    = info.len;
-        return;
-    }
-
-    if (!bh_file_exists(fc->filename)) {
-        onyx_report_error(fc->token->pos, Error_Critical,
-                "Unable to open file for reading, '%s'.",
-                fc->filename);
-        return;
-    }
-
-    // :RelativeFiles This should be relative to the current directory by default; However,
-    // if the filename is prefixed with a './' or '.\\' then it should be relative to the
-    // file in which is was inclded. The loaded file info above should probably use the full
-    // file path in order to avoid duplicates.
-    bh_file_contents contents = bh_file_read_contents(global_heap_allocator, fc->filename);
-    u8* actual_data = bh_alloc(global_heap_allocator, contents.length + 1);
-    u32 length = contents.length + 1;
-    memcpy(actual_data, contents.data, contents.length);
-    actual_data[contents.length] = 0;
-    bh_file_contents_free(&contents);
-
-    WasmDatum datum = {
-        .alignment = 16,
-        .length = length,
-        .data = actual_data,
-    };
-    fc->data_id = emit_data_entry(mod, &datum);
-    fc->size = length - 1;
-
-    shput(mod->loaded_file_info, fc->filename, ((StrLitInfo) {
-        .data_id = fc->data_id,
-        .len = fc->size,
-    }));
-}
-
-OnyxWasmModule onyx_wasm_module_create(bh_allocator alloc) {
-    OnyxWasmModule module = {
-        .allocator = alloc,
-
-        .type_map = NULL,
-        .next_type_idx = 0,
-        .types = NULL,
-
-        .funcs = NULL,
-        .next_func_idx = 0,
-
-        .exports = NULL,
-        .export_count = 0,
-
-        .imports = NULL,
-
-        .globals = NULL,
-        .next_global_idx = 0,
-
-        .data = NULL,
-        .data_patches = NULL,
-
-        .next_tls_offset = 0,
-        .tls_size_ptr = NULL,
-
-        .elems = NULL,
-        .next_elem_idx = 0,
-
-        .needs_memory_section = 0,
-        .memory_min_size = 0,
-        .memory_max_size = 0,
-
-        .structured_jump_target = NULL,
-        .return_location_stack = NULL,
-        .local_allocations = NULL,
-        .stack_leave_patches = NULL,
-        .deferred_stmts = NULL,
-
-        .heap_start_ptr = NULL,
-
-        .stack_top_ptr = NULL,
-        .stack_base_idx = 0,
-
-        .foreign_function_count = 0,
-
-        .null_proc_func_idx = -1,
-
-        .libraries = NULL,
-        .library_paths = NULL,
-
-        .foreign_blocks = NULL,
-        .next_foreign_block_idx = 0,
-
-        .procedures_with_tags = NULL
-    };
-
-    bh_arena* eid = bh_alloc(global_heap_allocator, sizeof(bh_arena));
-    bh_arena_init(eid, global_heap_allocator, 8196);
-    module.extended_instr_data = eid;
-    module.extended_instr_alloc = bh_arena_allocator(eid);
-
-    bh_arr_new(alloc, module.types, 4);
-    bh_arr_new(alloc, module.funcs, 4);
-    bh_arr_new(alloc, module.imports, 4);
-    bh_arr_new(alloc, module.globals, 4);
-    bh_arr_new(alloc, module.data, 4);
-    bh_arr_new(alloc, module.elems, 4);
-    bh_arr_new(alloc, module.libraries, 4);
-    bh_arr_new(alloc, module.library_paths, 4);
-
-    bh_arr_new(global_heap_allocator, module.return_location_stack, 4);
-    bh_arr_new(global_heap_allocator, module.structured_jump_target, 16);
-    bh_arr_set_length(module.structured_jump_target, 0);
-
-    sh_new_arena(module.type_map);
-    sh_new_arena(module.exports);
-    sh_new_arena(module.loaded_file_info);
-    sh_new_arena(module.string_literals);
-
-    bh_imap_init(&module.index_map, global_heap_allocator, 128);
-    bh_imap_init(&module.local_map, global_heap_allocator, 16);
-    bh_imap_init(&module.elem_map,  global_heap_allocator, 16);
-
-    bh_arr_new(global_heap_allocator, module.deferred_stmts, 4);
-    bh_arr_new(global_heap_allocator, module.local_allocations, 4);
-    bh_arr_new(global_heap_allocator, module.stack_leave_patches, 4);
-    bh_arr_new(global_heap_allocator, module.foreign_blocks, 4);
-    bh_arr_new(global_heap_allocator, module.procedures_with_tags, 4);
-    bh_arr_new(global_heap_allocator, module.data_patches, 4);
-
-#ifdef ENABLE_DEBUG_INFO
-    module.debug_context = bh_alloc_item(context.ast_alloc, DebugContext);
-    module.debug_context->allocator = global_heap_allocator;
-    module.debug_context->next_file_id = 0;
-    module.debug_context->next_sym_id = 0;
-    module.debug_context->last_token = NULL;
-
-    sh_new_arena(module.debug_context->file_info);
-    bh_arr_new(global_heap_allocator, module.debug_context->sym_info, 32);
-    bh_arr_new(global_heap_allocator, module.debug_context->sym_patches, 32);
-    bh_arr_new(global_heap_allocator, module.debug_context->funcs, 16);
-
-    bh_buffer_init(&module.debug_context->op_buffer, global_heap_allocator, 1024);
-#endif
-
-    return module;
-}
-
-void emit_entity(Entity* ent) {
-    OnyxWasmModule* module = context.wasm_module;
-    module->current_func_idx = -1;
-
-    switch (ent->type) {
-        case Entity_Type_Foreign_Function_Header:
-            if (!should_emit_function(ent->function)) break;
-
-            module->foreign_function_count++;
-            emit_foreign_function(module, ent->function);
-            // fallthrough
-
-        case Entity_Type_Function_Header:
-            if (!should_emit_function(ent->function)) break;
-
-            if (context.options->print_function_mappings) {
-                bh_printf("%d -> %s:%d:%d\n",
-                    module->next_func_idx,
-                    ent->expr->token->pos.filename,
-                    ent->expr->token->pos.line,
-                    ent->expr->token->pos.column);
-            }
-
-            bh_imap_put(&module->index_map, (u64) ent->function, module->next_func_idx++);
-
-            if (ent->function->flags & Ast_Flag_Proc_Is_Null) {
-                if (module->null_proc_func_idx == -1) module->null_proc_func_idx = get_element_idx(module, ent->function);
-            }
-
-            if (ent->function->tags != NULL) {
-                bh_arr_push(module->procedures_with_tags, ent->function);
-            }
-            break;
-
-        case Entity_Type_Global_Header:
-            bh_imap_put(&module->index_map, (u64) ent->global, module->next_global_idx++);
-            break;
-
-        case Entity_Type_String_Literal: {
-            emit_string_literal(module, (AstStrLit *) ent->strlit);
-            break;
-        }
-
-        case Entity_Type_File_Contents: {
-            emit_file_contents(module, (AstFileContents *) ent->file_contents);
-            break;
-        }
-
-        case Entity_Type_Memory_Reservation: {
-            emit_memory_reservation(module, (AstMemRes *) ent->mem_res);
-            break;
-        }
-
-        case Entity_Type_Process_Directive: {
-            if (ent->expr->kind == Ast_Kind_Directive_Export) {
-                emit_export_directive(module, (AstDirectiveExport *) ent->expr);
-            }
-
-            if (ent->expr->kind == Ast_Kind_Directive_Library) {
-                bh_arr_push(module->libraries, ent->library->library_name);
-            }
-            break;
-        }
-
-        case Entity_Type_Foreign_Block: {
-            ent->foreign_block->foreign_block_number = module->next_foreign_block_idx++;
-            bh_arr_push(module->foreign_blocks, (AstForeignBlock *) ent->foreign_block);
-            break;
-        }
-
-        case Entity_Type_Function: emit_function(module, ent->function); break;
-        case Entity_Type_Global:   emit_global(module,   ent->global); break;
-
-        // Cleanup: Maybe these should be printed elsewhere?
-        // Also, they should be sorted? Or have that ability?
-        case Entity_Type_Note: {
-            if (!context.options->print_notes) break;
-
-            AstNote* note = (AstNote *) ent->expr;
-            OnyxFilePos pos = note->token->pos;
-
-            bh_printf("Note: %b %s:%d:%d\n", note->token->text, note->token->length, pos.filename, pos.line, pos.column);
-
-            break;
-        }
-
-        default: break;
-    }
-
-    ent->state = Entity_State_Finalized;
-}
-
-void onyx_wasm_module_link(OnyxWasmModule *module, OnyxWasmLinkOptions *options) {
-    // If the pointer size is going to change,
-    // the code will probably need to be altered.
-    assert(POINTER_SIZE == 4);
-
-    module->memory_min_size = options->memory_min_size;
-    module->memory_max_size = options->memory_max_size;
-
-    if (context.options->use_multi_threading || options->import_memory) {
-        module->needs_memory_section = 0;
-
-        WasmImport mem_import = {
-            .kind   = WASM_FOREIGN_MEMORY,
-            .min    = options->memory_min_size,
-            .max    = options->memory_max_size, // NOTE: Why not use all 4 Gigs of memory?
-            .shared = context.options->runtime == Runtime_Js,
-
-            .mod    = options->import_memory_module_name,
-            .name   = options->import_memory_import_name,
-        };
-
-        bh_arr_push(module->imports, mem_import);
-
-    } else {
-        module->needs_memory_section = 1;
-    }
-
-    if (options->export_memory) {
-        WasmExport mem_export = {
-            .kind = WASM_FOREIGN_MEMORY,
-            .idx = 0,
-        };
-
-        shput(module->exports, options->export_memory_name, mem_export);
-        module->export_count++;
-    }
-
-    if (options->export_func_table) {
-        WasmExport func_table_export = {
-            .kind = WASM_FOREIGN_TABLE,
-            .idx  = 0,
-        };
-
-        shput(module->exports, options->export_func_table_name, func_table_export);
-        module->export_count++;
-    }
-
-    u32 datum_offset = options->null_reserve_size;
-    bh_arr_each(WasmDatum, datum, module->data) {
-        assert(datum->id > 0);
-
-        bh_align(datum_offset, datum->alignment);
-        datum->offset_ = datum_offset;
-
-        datum_offset += datum->length;
-    }
-
-    bh_arr_each(DatumPatchInfo, patch, module->data_patches) {
-        assert(patch->data_id > 0);
-        WasmDatum *datum = &module->data[patch->data_id - 1];
-        assert(datum->id == patch->data_id);
-
-        switch (patch->kind) {
-            case Datum_Patch_Instruction: {
-                WasmFunc *func = &module->funcs[patch->index - module->foreign_function_count];
-
-                assert(func->code[patch->location].type == WI_PTR_CONST);
-                func->code[patch->location].data.l = (u64) datum->offset_ + patch->offset;
-                break;
-            }
-
-            case Datum_Patch_Data: {
-                WasmDatum *datum_to_alter = &module->data[patch->index - 1];
-                assert(datum_to_alter->id == patch->index);
-
-                *((u32 *) bh_pointer_add(datum_to_alter->data, patch->location)) = (u32) datum->offset_ + patch->offset;
-                break;
-            }
-
-            case Datum_Patch_Relative: {
-                WasmDatum *datum_to_alter = &module->data[patch->index - 1];
-                assert(datum_to_alter->id == patch->index);
-
-                u32 *addr = (u32 *) bh_pointer_add(datum_to_alter->data, patch->location);
-                if (*addr != 0) {
-                    *addr += (u32) datum->offset_ + patch->offset;
-                }
-
-                break;
-            }
-
-            default: assert(0);
-        }
-    }
-
-    assert(module->stack_top_ptr && module->heap_start_ptr);
-
-    *module->stack_top_ptr = datum_offset;
-    bh_align(*module->stack_top_ptr, options->stack_alignment);
-
-    *module->heap_start_ptr = *module->stack_top_ptr + options->stack_size;
-    bh_align(*module->heap_start_ptr, 16);
-}
-
-void onyx_wasm_module_free(OnyxWasmModule* module) {
-    if (module->extended_instr_data != NULL)
-        bh_arena_free(module->extended_instr_data);
-
-    bh_arr_free(module->types);
-    bh_arr_free(module->funcs);
-    bh_imap_free(&module->local_map);
-    bh_imap_free(&module->index_map);
-    shfree(module->type_map);
-    shfree(module->exports);
-}
-
-
-b32 onyx_wasm_build_link_options_from_node(OnyxWasmLinkOptions *opts, AstTyped *node) {
-    node = (AstTyped *) strip_aliases((AstNode *) node);
-
-    assert(node && node->kind == Ast_Kind_Struct_Literal);
-    assert(builtin_link_options_type);
-
-    Type *link_options_type = type_build_from_ast(context.ast_alloc, builtin_link_options_type);
-    
-    AstStructLiteral *input = (AstStructLiteral *) node;
-
-    StructMember smem;
-    b32 out_is_valid;
-
-    // TODO: These should be properly error handled.
-    assert(type_lookup_member(link_options_type, "stack_first", &smem));
-    opts->stack_first = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid) != 0;
-    if (!out_is_valid) return 0;
-
-    assert(type_lookup_member(link_options_type, "stack_size", &smem));
-    opts->stack_size = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid);
-    if (!out_is_valid) return 0;
-
-    assert(type_lookup_member(link_options_type, "stack_alignment", &smem));
-    opts->stack_alignment = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid);
-    if (!out_is_valid) return 0;
-
-    assert(type_lookup_member(link_options_type, "null_reserve_size", &smem));
-    opts->null_reserve_size = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid);
-    if (!out_is_valid) return 0;
-
-    assert(type_lookup_member(link_options_type, "import_memory", &smem));
-    opts->import_memory = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid) != 0;
-    if (!out_is_valid) return 0;
-
-    assert(type_lookup_member(link_options_type, "import_memory_module_name", &smem));
-    opts->import_memory_module_name = get_expression_string_value(input->args.values[smem.idx], &out_is_valid);
-    if (!out_is_valid) return 0;
-
-    assert(type_lookup_member(link_options_type, "import_memory_import_name", &smem));
-    opts->import_memory_import_name = get_expression_string_value(input->args.values[smem.idx], &out_is_valid);
-    if (!out_is_valid) return 0;
-
-    assert(type_lookup_member(link_options_type, "export_memory", &smem));
-    opts->export_memory = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid) != 0;
-    if (!out_is_valid) return 0;
-
-    assert(type_lookup_member(link_options_type, "export_memory_name", &smem));
-    opts->export_memory_name = get_expression_string_value(input->args.values[smem.idx], &out_is_valid);
-    if (!out_is_valid) return 0;
-
-    assert(type_lookup_member(link_options_type, "export_func_table", &smem));
-    opts->export_func_table = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid) != 0;
-    if (!out_is_valid) return 0;
-
-    assert(type_lookup_member(link_options_type, "export_func_table_name", &smem));
-    opts->export_func_table_name = get_expression_string_value(input->args.values[smem.idx], &out_is_valid);
-    if (!out_is_valid) return 0;
-
-    assert(type_lookup_member(link_options_type, "memory_min_size", &smem));
-    opts->memory_min_size = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid);
-    if (!out_is_valid) return 0;
-
-    assert(type_lookup_member(link_options_type, "memory_max_size", &smem));
-    opts->memory_max_size = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid);
-    if (!out_is_valid) return 0;
-
-    return 1;
-}
-
-#include "wasm_output.h"
diff --git a/src/wasm_intrinsics.h b/src/wasm_intrinsics.h
deleted file mode 100644 (file)
index 49dae8c..0000000
+++ /dev/null
@@ -1,452 +0,0 @@
-// This file is directly included in src/onxywasm.c
-// It is here purely to decrease the amount of clutter in the main file.
-
-
-// IMPROVE: This implementation assumes that the source and destination buffers do not overlap.
-// The specification for memory.copy in WASM does work even if the buffers overlap.
-// Also, this implementation copies byte-by-byte, which is terrible. It should copy
-// quad word by quad word, and then the additional bytes if the count was not divisible by 8.
-// :32BitPointers
-EMIT_FUNC_NO_ARGS(intrinsic_memory_copy) {
-    bh_arr(WasmInstruction) code = *pcode;
-    
-    // The stack should look like this:
-    //     <count>
-    //     <source>
-    //     <dest>
-    
-    u64 count_local  = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
-    u64 source_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
-    u64 dest_local   = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
-    
-    WIL(NULL, WI_LOCAL_SET, count_local);
-    WIL(NULL, WI_LOCAL_SET, source_local);
-    WIL(NULL, WI_LOCAL_SET, dest_local);
-    
-    // count is greater than 0
-    WIL(NULL, WI_LOCAL_GET, count_local);
-    WID(NULL, WI_I32_CONST, 0);
-    WI(NULL, WI_I32_GT_S);
-    
-    WID(NULL, WI_IF_START, 0x40);
-    WID(NULL, WI_LOOP_START, 0x40);
-    
-    WIL(NULL, WI_LOCAL_GET, count_local);
-    WID(NULL, WI_I32_CONST, 1);
-    WI(NULL, WI_I32_SUB);
-    WIL(NULL, WI_LOCAL_SET, count_local);
-    
-    WIL(NULL, WI_LOCAL_GET, dest_local);
-    WIL(NULL, WI_LOCAL_GET, count_local);
-    WI(NULL, WI_PTR_ADD);
-    
-    WIL(NULL, WI_LOCAL_GET, source_local);
-    WIL(NULL, WI_LOCAL_GET, count_local);
-    WI(NULL, WI_PTR_ADD);
-    
-    WID(NULL, WI_I32_LOAD_8_U, ((WasmInstructionData) { 0, 0 }));
-    WID(NULL, WI_I32_STORE_8, ((WasmInstructionData) { 0, 0 }));
-    
-    WIL(NULL, WI_LOCAL_GET, count_local);
-    WID(NULL, WI_I32_CONST, 0);
-    WI(NULL, WI_I32_GT_S);
-    WID(NULL, WI_COND_JUMP, 0x00);
-    
-    WI(NULL, WI_LOOP_END);
-    WI(NULL, WI_IF_END);
-    
-    local_raw_free(mod->local_alloc, WASM_TYPE_INT32);
-    local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
-    local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
-    
-    *pcode = code;
-}
-
-EMIT_FUNC_NO_ARGS(intrinsic_memory_fill) {
-    bh_arr(WasmInstruction) code = *pcode;
-    
-    // The stack should look like this:
-    //     <count>
-    //     <byte>
-    //     <dest>
-    
-    u64 count_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
-    u64 byte_local  = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
-    u64 dest_local  = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
-    
-    WIL(NULL, WI_LOCAL_SET, count_local);
-    WIL(NULL, WI_LOCAL_SET, byte_local);
-    WIL(NULL, WI_LOCAL_SET, dest_local);
-    
-    // count is greater than 0
-    WIL(NULL, WI_LOCAL_GET, count_local);
-    WID(NULL, WI_I32_CONST, 0);
-    WI(NULL, WI_I32_GT_S);
-    
-    WID(NULL, WI_IF_START, 0x40);
-    WID(NULL, WI_LOOP_START, 0x40);
-    
-    WIL(NULL, WI_LOCAL_GET, count_local);
-    WID(NULL, WI_I32_CONST, 1);
-    WI(NULL, WI_I32_SUB);
-    WIL(NULL, WI_LOCAL_SET, count_local);
-    
-    WIL(NULL, WI_LOCAL_GET, dest_local);
-    WIL(NULL, WI_LOCAL_GET, count_local);
-    WI(NULL, WI_PTR_ADD);
-    
-    WIL(NULL, WI_LOCAL_GET, byte_local);
-    WID(NULL, WI_I32_STORE_8, ((WasmInstructionData) { 0, 0 }));
-    
-    WIL(NULL, WI_LOCAL_GET, count_local);
-    WID(NULL, WI_I32_CONST, 0);
-    WI(NULL, WI_I32_GT_S);
-    WID(NULL, WI_COND_JUMP, 0x00);
-    
-    WI(NULL, WI_LOOP_END);
-    WI(NULL, WI_IF_END);
-    
-    local_raw_free(mod->local_alloc, WASM_TYPE_INT32);
-    local_raw_free(mod->local_alloc, WASM_TYPE_INT32);
-    local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
-    
-    *pcode = code;
-}
-
-EMIT_FUNC(initialize_type, Type* type, OnyxToken* where) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    switch (type->kind) {
-        case Type_Kind_Pointer:
-        case Type_Kind_Basic: {
-            WasmType basic_type = onyx_type_to_wasm_type(type);
-            emit_zero_value(mod, &code, basic_type);
-            emit_store_instruction(mod, &code, type, 0);
-            break;
-        }
-
-        case Type_Kind_Struct: {
-            u64 value_ptr = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
-            WIL(NULL, WI_LOCAL_SET, value_ptr);
-
-            bh_arr_each(StructMember *, psmem, type->Struct.memarr) {
-                StructMember* smem = *psmem;
-                if (smem->initial_value == NULL || *smem->initial_value == NULL) continue;
-
-                WIL(NULL, WI_LOCAL_GET, value_ptr);
-                emit_expression(mod, &code, *smem->initial_value);
-                emit_store_instruction(mod, &code, smem->type, smem->offset);
-            }
-            
-            local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
-            break;
-        }
-
-        default:
-            onyx_report_error(where->pos, Error_Critical,
-                    "Unable to initialize type, '%s'. The reason for this is largely due to the compiler not knowing what the initial value should be.",
-                    type_get_name(type)); 
-            break;
-    }
-
-    *pcode = code;
-}
-
-EMIT_FUNC(intrinsic_atomic_wait, Type* type, OnyxToken* where) {
-    if (type->kind != Type_Kind_Basic) goto bad_type;
-
-    bh_arr(WasmInstruction) code = *pcode;
-
-    switch (type->Basic.kind) {
-        case Basic_Kind_I32:
-        case Basic_Kind_U32: WID(NULL, WI_ATOMIC_WAIT32, ((WasmInstructionData) { 2, 0 })); break;
-
-        case Basic_Kind_I64:
-        case Basic_Kind_U64: WID(NULL, WI_ATOMIC_WAIT64, ((WasmInstructionData) { 3, 0 })); break;
-
-        default: goto bad_type;
-    }
-
-    *pcode = code;
-    return;
-
-bad_type:
-    onyx_report_error(where->pos, Error_Critical, "Bad type for atomic wait, '%s'. Only i32 and i64 are supported.", type_get_name(type));
-}
-
-EMIT_FUNC_NO_ARGS(intrinsic_atomic_notify) {
-    bh_arr(WasmInstruction) code = *pcode;
-    WID(NULL, WI_ATOMIC_NOTIFY, ((WasmInstructionData) { 2, 0 }));
-    *pcode = code;
-}
-
-EMIT_FUNC_NO_ARGS(intrinsic_atomic_fence) {
-    bh_arr(WasmInstruction) code = *pcode;
-    WI(NULL, WI_ATOMIC_FENCE);
-    *pcode = code;
-}
-
-EMIT_FUNC(intrinsic_atomic_load, Type* type, OnyxToken* where) {
-    if (type->kind != Type_Kind_Basic) goto bad_type;
-
-    bh_arr(WasmInstruction) code = *pcode;
-
-    switch (type->Basic.kind) {
-        case Basic_Kind_U8:  WID(NULL, WI_ATOMIC_I32_LOAD8_U, ((WasmInstructionData) { 0, 0 })); break;
-        case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_LOAD16_U, ((WasmInstructionData) { 1, 0 })); break;
-
-        case Basic_Kind_I32:
-        case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_LOAD, ((WasmInstructionData) { 2, 0 })); break;
-
-        case Basic_Kind_I64:
-        case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_LOAD, ((WasmInstructionData) { 3, 0 })); break;
-
-        default: goto bad_type;
-    }
-
-    *pcode = code;
-    return;
-
-bad_type:
-    onyx_report_error(where->pos, Error_Critical, "Bad type for atomic load, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
-}
-
-EMIT_FUNC(intrinsic_atomic_store, Type* type, OnyxToken* where) {
-    if (type->kind != Type_Kind_Basic) goto bad_type;
-
-    bh_arr(WasmInstruction) code = *pcode;
-
-    switch (type->Basic.kind) {
-        case Basic_Kind_U8:  WID(NULL, WI_ATOMIC_I32_STORE8, ((WasmInstructionData) { 0, 0 })); break;
-        case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_STORE16, ((WasmInstructionData) { 1, 0 })); break;
-
-        case Basic_Kind_I32:
-        case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_STORE, ((WasmInstructionData) { 2, 0 })); break;
-
-        case Basic_Kind_I64:
-        case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_STORE, ((WasmInstructionData) { 3, 0 })); break;
-
-        default: goto bad_type;
-    }
-
-    *pcode = code;
-    return;
-
-bad_type:
-    onyx_report_error(where->pos, Error_Critical, "Bad type for atomic store, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
-}
-
-EMIT_FUNC(intrinsic_atomic_add, Type* type, OnyxToken* where) {
-    if (type->kind != Type_Kind_Basic) goto bad_type;
-
-    bh_arr(WasmInstruction) code = *pcode;
-
-    switch (type->Basic.kind) {
-        case Basic_Kind_U8:  WID(NULL, WI_ATOMIC_I32_ADD8_U, ((WasmInstructionData) { 0, 0 })); break;
-        case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_ADD16_U, ((WasmInstructionData) { 1, 0 })); break;
-
-        case Basic_Kind_I32:
-        case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_ADD, ((WasmInstructionData) { 2, 0 })); break;
-
-        case Basic_Kind_I64:
-        case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_ADD, ((WasmInstructionData) { 3, 0 })); break;
-
-        default: goto bad_type;
-    }
-
-    *pcode = code;
-    return;
-
-bad_type:
-    onyx_report_error(where->pos, Error_Critical, "Bad type for atomic add, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
-}
-
-EMIT_FUNC(intrinsic_atomic_sub, Type* type, OnyxToken* where) {
-    if (type->kind != Type_Kind_Basic) goto bad_type;
-
-    bh_arr(WasmInstruction) code = *pcode;
-
-    switch (type->Basic.kind) {
-        case Basic_Kind_U8:  WID(NULL, WI_ATOMIC_I32_SUB8_U, ((WasmInstructionData) { 0, 0 })); break;
-        case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_SUB16_U, ((WasmInstructionData) { 1, 0 })); break;
-
-        case Basic_Kind_I32:
-        case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_SUB, ((WasmInstructionData) { 2, 0 })); break;
-
-        case Basic_Kind_I64:
-        case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_SUB, ((WasmInstructionData) { 3, 0 })); break;
-
-        default: goto bad_type;
-    }
-
-    *pcode = code;
-    return;
-
-bad_type:
-    onyx_report_error(where->pos, Error_Critical, "Bad type for atomic sub, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
-}
-
-EMIT_FUNC(intrinsic_atomic_and, Type* type, OnyxToken* where) {
-    if (type->kind != Type_Kind_Basic) goto bad_type;
-
-    bh_arr(WasmInstruction) code = *pcode;
-
-    switch (type->Basic.kind) {
-        case Basic_Kind_U8:  WID(NULL, WI_ATOMIC_I32_AND8_U, ((WasmInstructionData) { 0, 0 })); break;
-        case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_AND16_U, ((WasmInstructionData) { 1, 0 })); break;
-
-        case Basic_Kind_I32:
-        case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_AND, ((WasmInstructionData) { 2, 0 })); break;
-
-        case Basic_Kind_I64:
-        case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_AND, ((WasmInstructionData) { 3, 0 })); break;
-
-        default: goto bad_type;
-    }
-
-    *pcode = code;
-    return;
-
-bad_type:
-    onyx_report_error(where->pos, Error_Critical, "Bad type for atomic and, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
-}
-
-EMIT_FUNC(intrinsic_atomic_or, Type* type, OnyxToken* where) {
-    if (type->kind != Type_Kind_Basic) goto bad_type;
-
-    bh_arr(WasmInstruction) code = *pcode;
-
-    switch (type->Basic.kind) {
-        case Basic_Kind_U8:  WID(NULL, WI_ATOMIC_I32_OR8_U, ((WasmInstructionData) { 0, 0 })); break;
-        case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_OR16_U, ((WasmInstructionData) { 1, 0 })); break;
-
-        case Basic_Kind_I32:
-        case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_OR, ((WasmInstructionData) { 2, 0 })); break;
-
-        case Basic_Kind_I64:
-        case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_OR, ((WasmInstructionData) { 3, 0 })); break;
-
-        default: goto bad_type;
-    }
-
-    *pcode = code;
-    return;
-
-bad_type:
-    onyx_report_error(where->pos, Error_Critical, "Bad type for atomic or, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
-}
-
-EMIT_FUNC(intrinsic_atomic_xor, Type* type, OnyxToken* where) {
-    if (type->kind != Type_Kind_Basic) goto bad_type;
-
-    bh_arr(WasmInstruction) code = *pcode;
-
-    switch (type->Basic.kind) {
-        case Basic_Kind_U8:  WID(NULL, WI_ATOMIC_I32_XOR8_U, ((WasmInstructionData) { 0, 0 })); break;
-        case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_XOR16_U, ((WasmInstructionData) { 1, 0 })); break;
-
-        case Basic_Kind_I32:
-        case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_XOR, ((WasmInstructionData) { 2, 0 })); break;
-
-        case Basic_Kind_I64:
-        case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_XOR, ((WasmInstructionData) { 3, 0 })); break;
-
-        default: goto bad_type;
-    }
-
-    *pcode = code;
-    return;
-
-bad_type:
-    onyx_report_error(where->pos, Error_Critical, "Bad type for atomic xor, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
-}
-
-EMIT_FUNC(intrinsic_atomic_xchg, Type* type, OnyxToken* where) {
-    if (type->kind != Type_Kind_Basic) goto bad_type;
-
-    bh_arr(WasmInstruction) code = *pcode;
-
-    switch (type->Basic.kind) {
-        case Basic_Kind_U8:  WID(NULL, WI_ATOMIC_I32_XCHG8_U, ((WasmInstructionData) { 0, 0 })); break;
-        case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_XCHG16_U, ((WasmInstructionData) { 1, 0 })); break;
-
-        case Basic_Kind_I32:
-        case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_XCHG, ((WasmInstructionData) { 2, 0 })); break;
-
-        case Basic_Kind_I64:
-        case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_XCHG, ((WasmInstructionData) { 3, 0 })); break;
-
-        default: goto bad_type;
-    }
-
-    *pcode = code;
-    return;
-
-bad_type:
-    onyx_report_error(where->pos, Error_Critical, "Bad type for atomic xchg, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
-}
-
-EMIT_FUNC(intrinsic_atomic_cmpxchg, Type* type, OnyxToken* where) {
-    if (type->kind != Type_Kind_Basic) goto bad_type;
-
-    bh_arr(WasmInstruction) code = *pcode;
-
-    switch (type->Basic.kind) {
-        case Basic_Kind_U8:  WID(NULL, WI_ATOMIC_I32_CMPXCHG8_U, ((WasmInstructionData) { 0, 0 })); break;
-        case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_CMPXCHG16_U, ((WasmInstructionData) { 1, 0 })); break;
-
-        case Basic_Kind_I32:
-        case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_CMPXCHG, ((WasmInstructionData) { 2, 0 })); break;
-
-        case Basic_Kind_I64:
-        case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_CMPXCHG, ((WasmInstructionData) { 3, 0 })); break;
-
-        default: goto bad_type;
-    }
-
-    *pcode = code;
-    return;
-
-bad_type:
-    onyx_report_error(where->pos, Error_Critical, "Bad type for atomic cmpxchg, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
-}
-
-EMIT_FUNC_NO_ARGS(initialize_data_segments_body) {
-    // :ProperLinking
-    if (!context.options->use_multi_threading || !context.options->use_post_mvp_features) return;
-
-    bh_arr(WasmInstruction) code = *pcode;
-
-    //
-    // Because this code is generated direction in the function
-    // it is assumed that EVERY data entry will be entered by
-    // this point. If data section entries can be entered after
-    // function body generation starts, this code will have to
-    // move to a link phase thing.
-    i32 index = 0;
-    bh_arr_each(WasmDatum, datum, mod->data) {
-        assert(datum->id > 0);
-        if (datum->data == NULL) { index++; continue; }
-
-        emit_data_relocation(mod, &code, datum->id);
-        WID(NULL, WI_PTR_CONST,   0);
-        WID(NULL, WI_I32_CONST,   datum->length);
-        WID(NULL, WI_MEMORY_INIT, ((WasmInstructionData) { index, 0 }));
-
-        index += 1;
-    }
-
-    *pcode = code;
-}
-
-EMIT_FUNC_NO_ARGS(run_init_procedures) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    bh_arr_each(AstFunction *, func, init_procedures) {
-        i32 func_idx = (i32) bh_imap_get(&mod->index_map, (u64) *func);
-        debug_emit_instruction(mod, NULL);
-        bh_arr_push(code, ((WasmInstruction){ WI_CALL, func_idx }));
-    }
-
-    *pcode = code;
-}
diff --git a/src/wasm_output.h b/src/wasm_output.h
deleted file mode 100644 (file)
index 74213c5..0000000
+++ /dev/null
@@ -1,1061 +0,0 @@
-// This file is included in src/onyxwasm.c.
-// It is separated because of its fundamentally different goals.
-
-//-------------------------------------------------
-// BINARY OUPUT
-//-------------------------------------------------
-
-#define WASM_SECTION_ID_CUSTOM 0
-#define WASM_SECTION_ID_TYPE 1
-#define WASM_SECTION_ID_IMPORT 2
-#define WASM_SECTION_ID_FUNCTION 3
-#define WASM_SECTION_ID_TABLE 4
-#define WASM_SECTION_ID_MEMORY 5
-#define WASM_SECTION_ID_GLOBAL 6
-#define WASM_SECTION_ID_EXPORT 7
-#define WASM_SECTION_ID_START 8
-#define WASM_SECTION_ID_ELEMENT 9
-#define WASM_SECTION_ID_DATACOUNT 12
-#define WASM_SECTION_ID_CODE 10
-#define WASM_SECTION_ID_DATA 11
-
-typedef i32 vector_func(void*, bh_buffer*);
-
-static const u8 ONYX_MAGIC_STRING[] = "ONYX";
-static const u8 WASM_MAGIC_STRING[] = { 0x00, 0x61, 0x73, 0x6D };
-static const u8 WASM_VERSION[] = { 0x01, 0x00, 0x00, 0x00 };
-
-static inline i32 output_unsigned_integer(u64 i, bh_buffer *buff) {
-    i32 leb_len;
-    u8* leb = uint_to_uleb128(i, &leb_len);
-    bh_buffer_append(buff, leb, leb_len);
-    return leb_len;
-}
-
-static inline i32 output_custom_section_name(char *name, bh_buffer *buff) {
-    u32 len = strlen(name);
-    i32 len_len = output_unsigned_integer(len, buff);
-    bh_buffer_append(buff, name, len);
-    return len_len + len;
-}
-
-static void output_instruction(WasmFunc* func, WasmInstruction* instr, bh_buffer* buff);
-
-static i32 output_vector(void** arr, i32 stride, i32 arrlen, vector_func elem, bh_buffer* vec_buff) {
-    i32 len;
-    u8* leb = uint_to_uleb128((u64) arrlen, &len);
-    bh_buffer_append(vec_buff, leb, len);
-
-    i32 i = 0;
-    while (i < arrlen) {
-        elem(*arr, vec_buff);
-        arr = bh_pointer_add(arr, stride);
-        i++;
-    }
-
-    return vec_buff->length;
-}
-
-static i32 output_name(const char* start, i32 length, bh_buffer* buff) {
-    i32 leb_len, prev_len = buff->length;
-    u8* leb = uint_to_uleb128((u64) length, &leb_len);
-    bh_buffer_append(buff, leb, leb_len);
-    bh_buffer_append(buff, start, length);
-    return buff->length - prev_len;
-}
-
-static i32 output_limits(i32 min, i32 max, b32 shared, bh_buffer* buff) {
-    i32 leb_len, prev_len = buff->length;
-    u8* leb;
-
-    u8 mem_type = 0x00;
-    if (max >= 0) mem_type |= 0x01;
-    if (shared)   mem_type |= 0x02;
-
-    bh_buffer_write_byte(buff, mem_type);
-
-    leb = uint_to_uleb128((u64) min, &leb_len);
-    bh_buffer_append(buff, leb, leb_len);
-
-    if (max >= 0) {
-        leb = uint_to_uleb128((u64) max, &leb_len);
-        bh_buffer_append(buff, leb, leb_len);
-    }
-
-    return buff->length - prev_len;
-}
-
-static i32 output_functype(WasmFuncType* type, bh_buffer* buff) {
-    i32 prev_len = buff->length;
-
-    bh_buffer_write_byte(buff, 0x60);
-
-    i32 len;
-    u8* leb_buff = uint_to_uleb128(type->param_count, &len);
-    bh_buffer_append(buff, leb_buff, len);
-    bh_buffer_append(buff, type->param_types, type->param_count);
-
-    if (type->return_type != WASM_TYPE_VOID) {
-        bh_buffer_write_byte(buff, 0x01);
-        bh_buffer_write_byte(buff, type->return_type);
-    } else {
-        bh_buffer_write_byte(buff, 0x00);
-    }
-
-    return buff->length - prev_len;
-}
-
-static i32 output_typesection(OnyxWasmModule* module, bh_buffer* buff) {
-    i32 prev_len = buff->length;
-    bh_buffer_write_byte(buff, 0x01);
-
-    bh_buffer vec_buff;
-    bh_buffer_init(&vec_buff, buff->allocator, 128);
-
-    i32 vec_len = output_vector(
-                                (void**) module->types,
-                                sizeof(WasmFuncType*),
-                                bh_arr_length(module->types),
-                                (vector_func *) output_functype,
-                                &vec_buff);
-
-    i32 leb_len;
-    u8* leb = uint_to_uleb128((u64) vec_len, &leb_len);
-    bh_buffer_append(buff, leb, leb_len);
-
-    bh_buffer_concat(buff, vec_buff);
-    bh_buffer_free(&vec_buff);
-
-    return buff->length - prev_len;
-}
-
-static i32 output_funcsection(OnyxWasmModule* module, bh_buffer* buff) {
-    i32 prev_len = buff->length;
-    bh_buffer_write_byte(buff, WASM_SECTION_ID_FUNCTION);
-
-    bh_buffer vec_buff;
-    bh_buffer_init(&vec_buff, buff->allocator, 128);
-
-    i32 leb_len;
-    u8* leb = uint_to_uleb128((u64) (bh_arr_length(module->funcs)), &leb_len);
-    bh_buffer_append(&vec_buff, leb, leb_len);
-
-    bh_arr_each(WasmFunc, func, module->funcs) {
-        leb = uint_to_uleb128((u64) (func->type_idx), &leb_len);
-        bh_buffer_append(&vec_buff, leb, leb_len);
-    }
-
-    leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
-    bh_buffer_append(buff, leb, leb_len);
-
-    bh_buffer_concat(buff, vec_buff);
-    bh_buffer_free(&vec_buff);
-
-    return buff->length - prev_len;
-}
-
-static i32 output_tablesection(OnyxWasmModule* module, bh_buffer* buff) {
-    // if (bh_arr_length(module->elems) == 0) return 0;
-
-    i32 prev_len = buff->length;
-    bh_buffer_write_byte(buff, WASM_SECTION_ID_TABLE);
-
-    bh_buffer vec_buff;
-    bh_buffer_init(&vec_buff, buff->allocator, 128);
-
-    i32 leb_len;
-    u8* leb = uint_to_uleb128((u64) 1, &leb_len);
-    bh_buffer_append(&vec_buff, leb, leb_len);
-
-    // NOTE: funcrefs are the only valid table element type
-    bh_buffer_write_byte(&vec_buff, 0x70);
-    output_limits(bh_arr_length(module->elems), -1, 0, &vec_buff);
-
-    leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
-    bh_buffer_append(buff, leb, leb_len);
-
-    bh_buffer_concat(buff, vec_buff);
-    bh_buffer_free(&vec_buff);
-
-    return buff->length - prev_len;
-}
-
-static i32 output_memorysection(OnyxWasmModule* module, bh_buffer* buff) {
-    // :ProperLinking
-    // if (context.options->use_multi_threading) return 0;
-    if (!module->needs_memory_section) return 0;
-
-    i32 prev_len = buff->length;
-    bh_buffer_write_byte(buff, WASM_SECTION_ID_MEMORY);
-
-    bh_buffer vec_buff;
-    bh_buffer_init(&vec_buff, buff->allocator, 128);
-
-    i32 leb_len;
-    u8* leb = uint_to_uleb128((u64) 1, &leb_len);
-    bh_buffer_append(&vec_buff, leb, leb_len);
-
-    output_limits(module->memory_min_size, -1, 0, &vec_buff);
-
-    leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
-    bh_buffer_append(buff, leb, leb_len);
-
-    bh_buffer_concat(buff, vec_buff);
-    bh_buffer_free(&vec_buff);
-
-    return buff->length - prev_len;
-}
-
-static i32 output_globalsection(OnyxWasmModule* module, bh_buffer* buff) {
-    i32 prev_len = buff->length;
-    bh_buffer_write_byte(buff, WASM_SECTION_ID_GLOBAL);
-
-    bh_buffer vec_buff;
-    bh_buffer_init(&vec_buff, buff->allocator, 128);
-
-    i32 leb_len;
-    u8* leb = uint_to_uleb128((u64) (bh_arr_length(module->globals)), &leb_len);
-    bh_buffer_append(&vec_buff, leb, leb_len);
-
-    bh_arr_each(WasmGlobal, global, module->globals) {
-        bh_buffer_write_byte(&vec_buff, global->type);
-        bh_buffer_write_byte(&vec_buff, 0x01);
-
-        bh_arr_each(WasmInstruction, instr, global->initial_value)
-            output_instruction(NULL, instr, &vec_buff);
-
-        // NOTE: Initial value expression terminator
-        bh_buffer_write_byte(&vec_buff, (u8) WI_BLOCK_END);
-    }
-
-    leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
-    bh_buffer_append(buff, leb, leb_len);
-
-    bh_buffer_concat(buff, vec_buff);
-    bh_buffer_free(&vec_buff);
-
-    return buff->length - prev_len;
-}
-
-static i32 output_importsection(OnyxWasmModule* module, bh_buffer* buff) {
-    i32 prev_len = buff->length;
-    bh_buffer_write_byte(buff, WASM_SECTION_ID_IMPORT);
-
-    bh_buffer vec_buff;
-    bh_buffer_init(&vec_buff, buff->allocator, 128);
-
-    i32 leb_len;
-    u8* leb = uint_to_uleb128((u64) (bh_arr_length(module->imports)), &leb_len);
-    bh_buffer_append(&vec_buff, leb, leb_len);
-
-    bh_arr_each(WasmImport, import, module->imports) {
-        output_name(import->mod, strlen(import->mod), &vec_buff);
-        output_name(import->name, strlen(import->name), &vec_buff);
-        bh_buffer_write_byte(&vec_buff, (u8) import->kind);
-
-        switch (import->kind) {
-            case WASM_FOREIGN_FUNCTION:
-                leb = uint_to_uleb128((u64) import->idx, &leb_len);
-                bh_buffer_append(&vec_buff, leb, leb_len);
-                break;
-
-            case WASM_FOREIGN_MEMORY:
-                output_limits(import->min, import->max, import->shared, &vec_buff);
-                break;
-
-            case WASM_FOREIGN_TABLE: assert(0);
-        }
-    }
-
-    leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
-    bh_buffer_append(buff, leb, leb_len);
-
-    bh_buffer_concat(buff, vec_buff);
-    bh_buffer_free(&vec_buff);
-
-    return buff->length - prev_len;
-}
-
-static i32 output_exportsection(OnyxWasmModule* module, bh_buffer* buff) {
-    i32 prev_len = buff->length;
-    bh_buffer_write_byte(buff, WASM_SECTION_ID_EXPORT);
-
-    bh_buffer vec_buff;
-    bh_buffer_init(&vec_buff, buff->allocator, 128);
-
-    i32 leb_len;
-    u8* leb = uint_to_uleb128((u64) (module->export_count), &leb_len);
-    bh_buffer_append(&vec_buff, leb, leb_len);
-
-    i32 key_len = 0;
-    fori (i, 0, shlen(module->exports)) {
-        char *key = module->exports[i].key;
-        WasmExport value = module->exports[i].value;
-
-        key_len = strlen(key);
-        output_name(key, key_len, &vec_buff);
-
-        bh_buffer_write_byte(&vec_buff, (u8) (value.kind));
-        leb = uint_to_uleb128((u64) value.idx, &leb_len);
-        bh_buffer_append(&vec_buff, leb, leb_len);
-    }
-
-    leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
-    bh_buffer_append(buff, leb, leb_len);
-
-    bh_buffer_concat(buff, vec_buff);
-    bh_buffer_free(&vec_buff);
-
-    return buff->length - prev_len;
-}
-
-static i32 output_startsection(OnyxWasmModule* module, bh_buffer* buff) {
-    i32 prev_len = buff->length;
-
-    i32 start_idx = -1;
-    fori (i, 0, shlen(module->exports)) {
-        char *key = module->exports[i].key;
-        WasmExport value = module->exports[i].value;
-
-        if (value.kind == WASM_FOREIGN_FUNCTION) {
-            if (strncmp("main", key, 5) == 0) {
-                start_idx = value.idx;
-                break;
-            }
-        }
-    }
-
-    if (start_idx != -1) {
-        bh_buffer_write_byte(buff, WASM_SECTION_ID_START);
-
-        i32 start_leb_len, section_leb_len;
-        uint_to_uleb128((u64) start_idx, &start_leb_len);
-        u8* section_leb = uint_to_uleb128((u64) start_leb_len, &section_leb_len);
-        bh_buffer_append(buff, section_leb, section_leb_len);
-
-        u8* start_leb = uint_to_uleb128((u64) start_idx, &start_leb_len);
-        bh_buffer_append(buff, start_leb, start_leb_len);
-    }
-
-    return buff->length - prev_len;
-}
-
-static i32 output_elemsection(OnyxWasmModule* module, bh_buffer* buff) {
-    if (bh_arr_length(module->elems) == 0) return 0;
-
-    i32 prev_len = buff->length;
-
-    bh_buffer_write_byte(buff, WASM_SECTION_ID_ELEMENT);
-
-    bh_buffer vec_buff;
-    bh_buffer_init(&vec_buff, buff->allocator, 128);
-
-    i32 leb_len;
-    u8* leb;
-
-    // NOTE: 0x01 count of elems
-    bh_buffer_write_byte(&vec_buff, 0x01);
-
-    // NOTE: 0x00 table index
-    bh_buffer_write_byte(&vec_buff, 0x00);
-
-    bh_buffer_write_byte(&vec_buff, WI_I32_CONST);
-    bh_buffer_write_byte(&vec_buff, 0x00);
-    bh_buffer_write_byte(&vec_buff, WI_BLOCK_END);
-
-    leb = uint_to_uleb128((u64) bh_arr_length(module->elems), &leb_len);
-    bh_buffer_append(&vec_buff, leb, leb_len);
-
-    bh_arr_each(i32, elem, module->elems) {
-        leb = uint_to_uleb128((u64) *elem, &leb_len);
-        bh_buffer_append(&vec_buff, leb, leb_len);
-    }
-
-    leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
-    bh_buffer_append(buff, leb, leb_len);
-
-    bh_buffer_concat(buff, vec_buff);
-    bh_buffer_free(&vec_buff);
-
-    return buff->length - prev_len;
-}
-
-static i32 output_locals(WasmFunc* func, bh_buffer* buff) {
-    i32 prev_len = buff->length;
-
-    // NOTE: Output vector length
-    i32 total_locals =
-        (i32) (func->locals.allocated[0] != 0) +
-        (i32) (func->locals.allocated[1] != 0) +
-        (i32) (func->locals.allocated[2] != 0) +
-        (i32) (func->locals.allocated[3] != 0) +
-        (i32) (func->locals.allocated[4] != 0);
-
-    i32 leb_len;
-    u8* leb = uint_to_uleb128((u64) total_locals, &leb_len);
-    bh_buffer_append(buff, leb, leb_len);
-
-    if (func->locals.allocated[0] != 0) {
-        leb = uint_to_uleb128((u64) func->locals.allocated[0], &leb_len);
-        bh_buffer_append(buff, leb, leb_len);
-        bh_buffer_write_byte(buff, WASM_TYPE_INT32);
-    }
-    if (func->locals.allocated[1] != 0) {
-        leb = uint_to_uleb128((u64) func->locals.allocated[1], &leb_len);
-        bh_buffer_append(buff, leb, leb_len);
-        bh_buffer_write_byte(buff, WASM_TYPE_INT64);
-    }
-    if (func->locals.allocated[2] != 0) {
-        leb = uint_to_uleb128((u64) func->locals.allocated[2], &leb_len);
-        bh_buffer_append(buff, leb, leb_len);
-        bh_buffer_write_byte(buff, WASM_TYPE_FLOAT32);
-    }
-    if (func->locals.allocated[3] != 0) {
-        leb = uint_to_uleb128((u64) func->locals.allocated[3], &leb_len);
-        bh_buffer_append(buff, leb, leb_len);
-        bh_buffer_write_byte(buff, WASM_TYPE_FLOAT64);
-    }
-    if (func->locals.allocated[4] != 0) {
-        leb = uint_to_uleb128((u64) func->locals.allocated[4], &leb_len);
-        bh_buffer_append(buff, leb, leb_len);
-        bh_buffer_write_byte(buff, WASM_TYPE_VAR128);
-    }
-
-    return buff->length - prev_len;
-}
-
-static void output_instruction(WasmFunc* func, WasmInstruction* instr, bh_buffer* buff) {
-    i32 leb_len;
-    u8* leb;
-
-    if (instr->type == WI_UNREACHABLE) assert(("EMITTING UNREACHABLE!!", 0));
-
-    if (instr->type == WI_NOP && !context.options->debug_enabled) return;
-
-    if (instr->type & SIMD_INSTR_MASK) {
-        bh_buffer_write_byte(buff, 0xFD);
-        leb = uint_to_uleb128((u64) (instr->type &~ SIMD_INSTR_MASK), &leb_len);
-        bh_buffer_append(buff, leb, leb_len);
-
-    } else if (instr->type & EXT_INSTR_MASK) {
-        bh_buffer_write_byte(buff, 0xFC);
-        leb = uint_to_uleb128((u64) (instr->type &~ EXT_INSTR_MASK), &leb_len);
-        bh_buffer_append(buff, leb, leb_len);
-
-    } else if (instr->type & ATOMIC_INSTR_MASK) {
-        bh_buffer_write_byte(buff, 0xFE);
-        leb = uint_to_uleb128((u64) (instr->type &~ ATOMIC_INSTR_MASK), &leb_len);
-        bh_buffer_append(buff, leb, leb_len);
-
-        if (instr->type == WI_ATOMIC_FENCE) {
-            bh_buffer_write_byte(buff, 0x00);
-
-        } else {
-            leb = uint_to_uleb128((u64) instr->data.i1, &leb_len);
-            bh_buffer_append(buff, leb, leb_len);
-
-            leb = uint_to_uleb128((u64) instr->data.i2, &leb_len);
-            bh_buffer_append(buff, leb, leb_len);
-        }
-
-    } else {
-        bh_buffer_write_byte(buff, (u8) instr->type);
-    }
-
-    switch (instr->type) {
-        case WI_LOCAL_GET:
-        case WI_LOCAL_SET:
-        case WI_LOCAL_TEE: {
-            u64 actual_idx = local_lookup_idx(&func->locals, instr->data.l);
-            leb = uint_to_uleb128(actual_idx, &leb_len);
-            bh_buffer_append(buff, leb, leb_len);
-            break;
-        }
-
-        case WI_GLOBAL_GET:
-        case WI_GLOBAL_SET:
-        case WI_CALL:
-        case WI_BLOCK_START:
-        case WI_LOOP_START:
-        case WI_JUMP:
-        case WI_COND_JUMP:
-        case WI_IF_START:
-        case WI_MEMORY_SIZE:
-        case WI_MEMORY_GROW:
-        case WI_MEMORY_FILL:
-            leb = uint_to_uleb128((u64) instr->data.i1, &leb_len);
-            bh_buffer_append(buff, leb, leb_len);
-            break;
-
-        case WI_MEMORY_INIT:
-        case WI_MEMORY_COPY:
-            leb = uint_to_uleb128((u64) instr->data.i1, &leb_len);
-            bh_buffer_append(buff, leb, leb_len);
-
-            leb = uint_to_uleb128((u64) instr->data.i2, &leb_len);
-            bh_buffer_append(buff, leb, leb_len);
-            break;
-
-        case WI_JUMP_TABLE: {
-            BranchTable* bt = (BranchTable *) instr->data.p;
-
-            leb = uint_to_uleb128((u64) bt->count, &leb_len);
-            bh_buffer_append(buff, leb, leb_len);
-
-            fori (i, 0, bt->count) {
-                leb = uint_to_uleb128((u64) bt->cases[i], &leb_len);
-                bh_buffer_append(buff, leb, leb_len);
-            }
-
-            leb = uint_to_uleb128((u64) bt->default_case, &leb_len);
-            bh_buffer_append(buff, leb, leb_len);
-            break;
-        }
-
-
-        case WI_CALL_INDIRECT:
-        case WI_I32_STORE: case WI_I32_STORE_8: case WI_I32_STORE_16:
-        case WI_I64_STORE: case WI_I64_STORE_8: case WI_I64_STORE_16: case WI_I64_STORE_32:
-        case WI_F32_STORE: case WI_F64_STORE:
-        case WI_V128_STORE:
-        case WI_I32_LOAD:
-        case WI_I32_LOAD_8_S: case WI_I32_LOAD_8_U:
-        case WI_I32_LOAD_16_S: case WI_I32_LOAD_16_U:
-        case WI_I64_LOAD:
-        case WI_I64_LOAD_8_S: case WI_I64_LOAD_8_U:
-        case WI_I64_LOAD_16_S: case WI_I64_LOAD_16_U:
-        case WI_I64_LOAD_32_S: case WI_I64_LOAD_32_U:
-        case WI_F32_LOAD: case WI_F64_LOAD:
-        case WI_V128_LOAD:
-            leb = uint_to_uleb128((u64) instr->data.i1, &leb_len);
-            bh_buffer_append(buff, leb, leb_len);
-            leb = uint_to_uleb128((u64) instr->data.i2, &leb_len);
-            bh_buffer_append(buff, leb, leb_len);
-            break;
-
-        case WI_I32_CONST:
-            leb = int_to_leb128((i64) instr->data.i1, &leb_len);
-            bh_buffer_append(buff, leb, leb_len);
-            break;
-        case WI_I64_CONST:
-            leb = int_to_leb128((i64) instr->data.l, &leb_len);
-            bh_buffer_append(buff, leb, leb_len);
-            break;
-        case WI_F32_CONST:
-            leb = float_to_ieee754(instr->data.f, 0);
-            bh_buffer_append(buff, leb, 4);
-            break;
-        case WI_F64_CONST:
-            leb = double_to_ieee754(instr->data.d, 0);
-            bh_buffer_append(buff, leb, 8);
-            break;
-
-        case WI_V128_CONST:
-        case WI_I8X16_SHUFFLE:
-            fori (i, 0, 16) bh_buffer_write_byte(buff, ((u8*) instr->data.p)[i]);
-            break;
-
-        case WI_I8X16_EXTRACT_LANE_S: case WI_I8X16_EXTRACT_LANE_U: case WI_I8X16_REPLACE_LANE:
-        case WI_I16X8_EXTRACT_LANE_S: case WI_I16X8_EXTRACT_LANE_U: case WI_I16X8_REPLACE_LANE:
-        case WI_I32X4_EXTRACT_LANE: case WI_I32X4_REPLACE_LANE:
-        case WI_I64X2_EXTRACT_LANE: case WI_I64X2_REPLACE_LANE:
-        case WI_F32X4_EXTRACT_LANE: case WI_F32X4_REPLACE_LANE:
-        case WI_F64X2_EXTRACT_LANE: case WI_F64X2_REPLACE_LANE:
-            bh_buffer_write_byte(buff, (u8) instr->data.i1);
-            break;
-
-        default: break;
-    }
-}
-
-static i32 output_code(WasmFunc* func, bh_buffer* buff) {
-
-    bh_buffer code_buff;
-    bh_buffer_init(&code_buff, buff->allocator, 128);
-
-    // Output locals
-    output_locals(func, &code_buff);
-
-    assert(func->code);
-
-    // Output code
-    bh_arr_each(WasmInstruction, instr, func->code) output_instruction(func, instr, &code_buff);
-
-    i32 leb_len;
-    u8* leb = uint_to_uleb128((u64) code_buff.length, &leb_len);
-    bh_buffer_append(buff, leb, leb_len);
-
-    bh_buffer_concat(buff, code_buff);
-    bh_buffer_free(&code_buff);
-
-    return 0;
-}
-
-static i32 output_codesection(OnyxWasmModule* module, bh_buffer* buff) {
-    i32 prev_len = buff->length;
-
-    bh_buffer_write_byte(buff, WASM_SECTION_ID_CODE);
-
-    bh_buffer vec_buff;
-    bh_buffer_init(&vec_buff, buff->allocator, 128);
-
-    i32 leb_len;
-    u8* leb = uint_to_uleb128((u64) bh_arr_length(module->funcs), &leb_len);
-    bh_buffer_append(&vec_buff, leb, leb_len);
-
-    bh_arr_each(WasmFunc, func, module->funcs) output_code(func, &vec_buff);
-
-    leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
-    bh_buffer_append(buff, leb, leb_len);
-
-    bh_buffer_concat(buff, vec_buff);
-    bh_buffer_free(&vec_buff);
-
-    return buff->length - prev_len;
-}
-
-static i32 output_datacountsection(OnyxWasmModule* module, bh_buffer* buff) {
-    if (!context.options->use_post_mvp_features) return 0;
-
-    i32 prev_len = buff->length;
-
-    bh_buffer_write_byte(buff, WASM_SECTION_ID_DATACOUNT);
-
-    bh_buffer vec_buff;
-    bh_buffer_init(&vec_buff, buff->allocator, 128);
-
-    i32 leb_len;
-    u8* leb = uint_to_uleb128((u64) bh_arr_length(module->data), &leb_len);
-    bh_buffer_append(&vec_buff, leb, leb_len);
-
-    leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
-    bh_buffer_append(buff, leb, leb_len);
-
-    bh_buffer_concat(buff, vec_buff);
-    bh_buffer_free(&vec_buff);
-
-    return buff->length - prev_len;
-}
-
-static i32 output_datasection(OnyxWasmModule* module, bh_buffer* buff) {
-    i32 prev_len = buff->length;
-
-    bh_buffer_write_byte(buff, WASM_SECTION_ID_DATA);
-
-    bh_buffer vec_buff;
-    bh_buffer_init(&vec_buff, buff->allocator, 128);
-
-    i32 leb_len;
-    u8* leb = uint_to_uleb128((u64) bh_arr_length(module->data), &leb_len);
-    bh_buffer_append(&vec_buff, leb, leb_len);
-
-    bh_arr_each(WasmDatum, datum, module->data) {
-        i32 memory_flags = 0x00;
-        // :ProperLinking
-        if (context.options->use_multi_threading) memory_flags |= 0x01;
-
-        bh_buffer_write_byte(&vec_buff, memory_flags);
-
-        // :ProperLinking
-        if (!context.options->use_multi_threading) {
-            bh_buffer_write_byte(&vec_buff, WI_I32_CONST);
-            leb = int_to_leb128((i64) datum->offset_, &leb_len);
-            bh_buffer_append(&vec_buff, leb, leb_len);
-            bh_buffer_write_byte(&vec_buff, WI_BLOCK_END);
-        }
-
-        if (datum->data != NULL) {
-            leb = uint_to_uleb128((u64) datum->length, &leb_len);
-            bh_buffer_append(&vec_buff, leb, leb_len);
-            fori (i, 0, datum->length) bh_buffer_write_byte(&vec_buff, ((u8 *) datum->data)[i]);
-        } else {
-            leb = uint_to_uleb128(0, &leb_len);
-            bh_buffer_append(&vec_buff, leb, leb_len);
-        }
-    }
-
-    leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
-    bh_buffer_append(buff, leb, leb_len);
-
-    bh_buffer_concat(buff, vec_buff);
-    bh_buffer_free(&vec_buff);
-
-    return buff->length - prev_len;
-}
-
-static i32 output_onyx_libraries_section(OnyxWasmModule* module, bh_buffer* buff) {
-    if (bh_arr_length(module->libraries) == 0) return 0;
-    i32 prev_len = buff->length;
-
-    bh_buffer_write_byte(buff, WASM_SECTION_ID_CUSTOM);
-
-    bh_buffer libs_buff;
-    bh_buffer_init(&libs_buff, buff->allocator, 128);
-
-    output_custom_section_name("_onyx_libs", &libs_buff);
-
-    output_unsigned_integer(bh_arr_length(module->library_paths), &libs_buff);
-
-    bh_arr_each(char *, lib, module->library_paths) {
-        assert(*lib != NULL);
-
-        u32 lib_len = strlen(*lib);
-        output_unsigned_integer(lib_len, &libs_buff);
-        bh_buffer_append(&libs_buff, *lib, lib_len);
-    }
-
-    output_unsigned_integer(bh_arr_length(module->libraries), &libs_buff);
-
-    bh_arr_each(char *, lib, module->libraries) {
-        assert(*lib != NULL);
-
-        u32 lib_len = strlen(*lib);
-        output_unsigned_integer(lib_len, &libs_buff);
-        bh_buffer_append(&libs_buff, *lib, lib_len);
-    }
-
-    output_unsigned_integer(libs_buff.length, buff);
-
-    bh_buffer_concat(buff, libs_buff);
-    bh_buffer_free(&libs_buff);
-
-    return buff->length - prev_len;
-}
-
-static i32 output_onyx_func_offset_section(OnyxWasmModule* module, bh_buffer* buff) {
-    i32 prev_len = buff->length;
-
-    bh_buffer_write_byte(buff, WASM_SECTION_ID_CUSTOM);
-
-    bh_buffer section_buff;
-    bh_buffer_init(&section_buff, buff->allocator, 128);
-
-    output_custom_section_name("_onyx_func_offsets", &section_buff);
-
-    i32 func_count = bh_arr_length(module->funcs) + module->foreign_function_count;
-
-    bh_buffer name_buff;
-    bh_buffer_init(&name_buff, buff->allocator, 1024);
-    u32 str_cursor = func_count * 4;
-    fori (i, 0, func_count) {
-        bh_buffer_write_u32(&section_buff, str_cursor);
-
-        if (i < module->foreign_function_count) {
-            bh_buffer_append(&name_buff, "<imported function>", 20);
-            str_cursor += 20;
-        } else {
-            WasmFunc *func = &module->funcs[i - module->foreign_function_count];
-            assert(func->location);
-            char *str = bh_bprintf("%s:%d,%d\0", func->location->pos.filename, func->location->pos.line, func->location->pos.column);
-            i32 len = strlen(str);
-            bh_buffer_append(&name_buff, str, len + 1);
-            str_cursor += len + 1;
-        }
-    }
-
-    bh_buffer_concat(&section_buff, name_buff);
-
-    output_unsigned_integer(section_buff.length, buff);
-
-    bh_buffer_concat(buff, section_buff);
-    bh_buffer_free(&section_buff);
-
-    return buff->length - prev_len;
-}
-
-#ifdef ENABLE_DEBUG_INFO
-static i32 output_ovm_debug_sections(OnyxWasmModule* module, bh_buffer* buff) {
-    if (!module->debug_context || !context.options->debug_enabled) return 0;
-
-    DebugContext *ctx = module->debug_context;
-
-    bh_buffer section_buff;
-    bh_buffer_init(&section_buff, buff->allocator, 128);
-
-    {
-        // ovm_debug_files section
-        bh_buffer_clear(&section_buff);
-        bh_buffer_write_byte(buff, WASM_SECTION_ID_CUSTOM);
-
-        output_custom_section_name("ovm_debug_files", &section_buff);
-
-        i32 file_count = shlenu(ctx->file_info);
-        output_unsigned_integer(file_count, &section_buff);
-
-        fori (i, 0, file_count) {
-            Table(DebugFileInfo) entry = (void *) &ctx->file_info[i];
-            output_unsigned_integer(entry->value.file_id, &section_buff);
-            output_unsigned_integer(entry->value.line_count, &section_buff);
-            output_name(entry->key, strlen(entry->key), &section_buff);
-        }
-
-        output_unsigned_integer(section_buff.length, buff);
-
-        bh_buffer_concat(buff, section_buff);
-    }
-
-    {
-        // ovm_debug_funcs section
-        bh_buffer_clear(&section_buff);
-        bh_buffer_write_byte(buff, WASM_SECTION_ID_CUSTOM);
-
-        output_custom_section_name("ovm_debug_funcs", &section_buff);
-
-        i32 func_count = bh_arr_length(ctx->funcs);
-        output_unsigned_integer(func_count, &section_buff);
-
-        fori (i, 0, func_count) {
-            DebugFuncContext *func = &ctx->funcs[i];
-            output_unsigned_integer(func->func_index, &section_buff);
-            output_unsigned_integer(func->file_id, &section_buff);
-            output_unsigned_integer(func->line, &section_buff);
-            output_name(func->name, func->name_length, &section_buff);
-            output_unsigned_integer(1, &section_buff);
-            output_unsigned_integer(func->op_offset, &section_buff);
-
-            LocalAllocator *locals = &module->funcs[i].locals;
-            if (func->stack_ptr_idx > 0) {
-                u32 local_idx = local_lookup_idx(locals, func->stack_ptr_idx);
-                output_unsigned_integer(local_idx, &section_buff);
-            } else {
-                output_unsigned_integer(0, &section_buff);
-            }
-
-            output_unsigned_integer(0, &section_buff);
-        }
-
-        output_unsigned_integer(section_buff.length, buff);
-
-        bh_buffer_concat(buff, section_buff);
-    }
-
-    {
-        // ovm_debug_syms section
-
-        // First, apply patches for register locations
-        bh_arr_each(DebugSymPatch, patch, ctx->sym_patches) {
-            // CLEANUP: This is (kind of) incorrect, as there is nothing guarenteeing
-            // that the symbol with id a will be a position a, other than the way
-            // that this has been implemented right now.
-            assert(ctx->sym_info[patch->sym_id].location_type == DSL_REGISTER);
-
-            LocalAllocator *locals = &module->funcs[patch->func_idx - module->foreign_function_count].locals;
-            ctx->sym_info[patch->sym_id].location_num = local_lookup_idx(locals, patch->local_idx);
-        }
-
-        bh_buffer_clear(&section_buff);
-        bh_buffer_write_byte(buff, WASM_SECTION_ID_CUSTOM);
-
-        output_custom_section_name("ovm_debug_syms", &section_buff);
-
-        i32 sym_count = bh_arr_length(ctx->sym_info);
-        output_unsigned_integer(sym_count, &section_buff);
-
-        fori (i, 0, sym_count) {
-            DebugSymInfo *sym = &ctx->sym_info[i];
-            output_unsigned_integer(sym->sym_id, &section_buff);
-            if (sym->name) {
-                output_name(sym->name, strlen(sym->name), &section_buff);
-            } else {
-                output_unsigned_integer(0, &section_buff);
-            }
-            output_unsigned_integer(sym->location_type, &section_buff);
-            output_unsigned_integer(sym->location_num, &section_buff);
-            output_unsigned_integer(sym->type, &section_buff);
-        }
-
-        output_unsigned_integer(section_buff.length, buff);
-
-        bh_buffer_concat(buff, section_buff);
-    }
-
-    {
-        // ovm_debug_types section
-        bh_buffer_clear(&section_buff);
-        bh_buffer_write_byte(buff, WASM_SECTION_ID_CUSTOM);
-
-        output_custom_section_name("ovm_debug_types", &section_buff);
-
-        i32 type_count = bh_arr_length(type_map.entries);
-        output_unsigned_integer(type_count, &section_buff);
-
-        bh_arr_each(bh__imap_entry, entry, type_map.entries) {
-            u32 id     = entry->key;
-            Type *type = (Type *) entry->value;
-            const char *name = type_get_name(type);
-
-            output_unsigned_integer(id, &section_buff);
-            output_name(name, strlen(name), &section_buff);
-            output_unsigned_integer(type_size_of(type), &section_buff);
-
-            if (type->kind == Type_Kind_Basic) {
-                // Type indicies are special because they are encoded
-                // as a "distinct" unsigned 32-bit integer, which is
-                // effectively how they are used in the code anyway.
-                //
-                // This is probably a change that will be made throughout
-                // the entire compiler, but for now they will remain as
-                // a special type.
-                if (type->Basic.kind == Basic_Kind_Type_Index) {
-                    output_unsigned_integer(5, &section_buff);
-                    output_unsigned_integer(2, &section_buff);
-                    output_unsigned_integer(basic_types[Basic_Kind_U32].id, &section_buff);
-                    continue;
-                }
-
-                if (type->Basic.kind == Basic_Kind_Rawptr) {
-                    // rawptr -> ^void
-                    output_unsigned_integer(2, &section_buff);
-                    output_unsigned_integer(1, &section_buff);
-                    output_unsigned_integer(basic_types[Basic_Kind_Void].id, &section_buff);
-                    continue;
-                }
-
-                output_unsigned_integer(1, &section_buff);
-                if      (type->Basic.kind == Basic_Kind_Void) output_unsigned_integer(0, &section_buff);
-                else if (type_is_bool(type))                  output_unsigned_integer(4, &section_buff);
-                else if (type_is_integer(type)) {
-                    if (type->Basic.flags & Basic_Flag_Unsigned) output_unsigned_integer(2, &section_buff);
-                    else                                         output_unsigned_integer(1, &section_buff);
-                }
-                else if (type->Basic.flags & Basic_Flag_Float)   output_unsigned_integer(3, &section_buff);
-                else if (type_is_simd(type))                     output_unsigned_integer(6, &section_buff);
-                else {
-                    output_unsigned_integer(0, &section_buff);
-                }
-
-                continue;
-            }
-
-            if (type->kind == Type_Kind_Pointer) {
-                output_unsigned_integer(2, &section_buff);
-                output_unsigned_integer(1, &section_buff);
-                output_unsigned_integer(type->Pointer.elem->id, &section_buff);
-                continue; 
-            }
-
-            if (type->kind == Type_Kind_Enum) {
-                output_unsigned_integer(5, &section_buff);
-                output_unsigned_integer(2, &section_buff);
-                output_unsigned_integer(type->Enum.backing->id, &section_buff);
-                continue;
-            }
-
-            if (type->kind == Type_Kind_Array) {
-                output_unsigned_integer(4, &section_buff);
-                output_unsigned_integer(type->Array.count, &section_buff);
-                output_unsigned_integer(type->Array.elem->id, &section_buff);
-                continue;
-            }
-
-            if (type_is_structlike_strict(type)) {
-                output_unsigned_integer(3, &section_buff);
-
-                i32 mem_count = type_structlike_mem_count(type);
-                output_unsigned_integer(mem_count, &section_buff);
-
-                fori (i, 0, mem_count) {
-                    StructMember smem;
-                    type_lookup_member_by_idx(type, i, &smem);
-
-                    output_unsigned_integer(smem.offset, &section_buff);
-                    output_unsigned_integer(smem.type->id, &section_buff);
-                    output_name(smem.name, strlen(smem.name), &section_buff);
-                }
-
-                continue;
-            }
-
-            if (type->kind == Type_Kind_Function) {
-                output_unsigned_integer(6, &section_buff);
-                output_unsigned_integer(type->Function.param_count, &section_buff);
-
-                fori (i, 0, (i32) type->Function.param_count) {
-                    output_unsigned_integer(type->Function.params[i]->id, &section_buff);
-                }
-
-                output_unsigned_integer(type->Function.return_type->id, &section_buff);
-                continue;
-            }
-
-            if (type->kind == Type_Kind_Distinct) {
-                output_unsigned_integer(5, &section_buff);
-                output_unsigned_integer(2, &section_buff);
-                output_unsigned_integer(type->Distinct.base_type->id, &section_buff);
-                continue;
-            }
-
-            // No debug information will be given about the poly struct
-            // or compound types.
-            // Outside of runtime type information, they provide no useful
-            // debugging information (I don't think at least...).
-            if (type->kind == Type_Kind_PolyStruct ||
-                type->kind == Type_Kind_Compound) {
-                output_unsigned_integer(1, &section_buff);
-                output_unsigned_integer(0, &section_buff);
-                continue;
-            }
-
-            assert(("Unhandled type", 0));
-        }
-
-        output_unsigned_integer(section_buff.length, buff);
-
-        bh_buffer_concat(buff, section_buff);
-    }
-
-    {
-        // ovm_debug_ops section
-        bh_buffer_clear(&section_buff);
-        bh_buffer_write_byte(buff, WASM_SECTION_ID_CUSTOM);
-
-        output_custom_section_name("ovm_debug_ops", &section_buff);
-        bh_buffer_concat(&section_buff, ctx->op_buffer);
-
-        output_unsigned_integer(section_buff.length, buff);
-
-        bh_buffer_concat(buff, section_buff);
-    }
-
-    bh_buffer_free(&section_buff);
-    return 0;
-}
-#endif
-
-void onyx_wasm_module_write_to_buffer(OnyxWasmModule* module, bh_buffer* buffer) {
-    bh_buffer_init(buffer, global_heap_allocator, 128);
-    if (context.options->runtime == Runtime_Onyx) {
-        bh_buffer_append(buffer, ONYX_MAGIC_STRING, 4);
-    } else {
-        bh_buffer_append(buffer, WASM_MAGIC_STRING, 4);
-    }
-    bh_buffer_append(buffer, WASM_VERSION, 4);
-
-#ifdef ENABLE_DEBUG_INFO
-    output_ovm_debug_sections(module, buffer);
-#endif
-    output_typesection(module, buffer);
-    output_importsection(module, buffer);
-    output_funcsection(module, buffer);
-    output_tablesection(module, buffer);
-    output_memorysection(module, buffer);
-    output_globalsection(module, buffer);
-    output_exportsection(module, buffer);
-    output_startsection(module, buffer);
-    output_elemsection(module, buffer);
-    output_datacountsection(module, buffer);
-    output_codesection(module, buffer);
-    output_datasection(module, buffer);
-    output_onyx_libraries_section(module, buffer);
-
-    // TODO: Consider if this should always be included?
-    // It can amount to a lot of extra data.
-    output_onyx_func_offset_section(module, buffer);
-}
-
-void onyx_wasm_module_write_to_file(OnyxWasmModule* module, bh_file file) {
-    bh_buffer master_buffer;
-    onyx_wasm_module_write_to_buffer(module, &master_buffer);
-
-    bh_file_write(&file, master_buffer.data, master_buffer.length);
-}
diff --git a/src/wasm_runtime.c b/src/wasm_runtime.c
deleted file mode 100644 (file)
index 09797d4..0000000
+++ /dev/null
@@ -1,434 +0,0 @@
-#include "bh.h"
-#include "utils.h"
-#include "astnodes.h"
-#include "wasm.h"
-#include "onyx_library.h"
-
-#ifndef USE_OVM_DEBUGGER
-    #include "wasmer.h"
-#endif
-
-#ifdef _BH_LINUX
-    #include <pthread.h>
-    #include <signal.h>
-    #include <sys/wait.h>
-    #include <dlfcn.h>
-#endif
-
-static wasm_config_t*    wasm_config;
-static wasm_engine_t*    wasm_engine;
-static wasm_store_t*     wasm_store;
-static wasm_extern_vec_t wasm_imports;
-static bh_buffer         wasm_raw_bytes;
-wasm_instance_t*  wasm_instance;
-wasm_module_t*    wasm_module;
-wasm_memory_t*    wasm_memory;
-
-OnyxRuntime wasm_runtime;
-
-b32 wasm_name_equals(const wasm_name_t* name1, const wasm_name_t* name2) {
-    if (name1->size != name2->size) return 0;
-    return !strncmp(name1->data, name2->data, name1->size);
-}
-
-b32 wasm_name_equals_string(const wasm_name_t* name1, const char* name2) {
-    u32 name2_size = strlen(name2);
-    if (name1->size != name2_size) return 0;
-    return !strncmp(name1->data, name2, name1->size);
-}
-
-wasm_extern_t* wasm_extern_lookup_by_name(wasm_module_t* module, wasm_instance_t* instance, const char* name) {
-    i32 name_len = strlen(name);
-
-    i32 idx = -1;
-    wasm_exporttype_vec_t export_types;
-    wasm_module_exports(module, &export_types);
-    fori (i, 0, (i64) export_types.size) {
-        wasm_exporttype_t* export_type = export_types.data[i];
-        const wasm_name_t* export_name = wasm_exporttype_name(export_type);
-
-        if (!strncmp(export_name->data, name, name_len)) {
-            idx = i;
-            break;
-        }
-    }
-
-    if (idx == -1) return NULL;
-
-    wasm_extern_vec_t exports;
-    wasm_instance_exports(instance, &exports);
-
-    return exports.data[idx];
-}
-
-
-
-typedef void *(*LibraryLinker)(OnyxRuntime *runtime);
-
-typedef struct LibraryLinkContext {
-    bh_buffer wasm_bytes;
-    bh_arr(char *) library_paths;
-} LibraryLinkContext;
-
-static WasmFuncDefinition** onyx_load_library(LibraryLinkContext *ctx, char *name) {
-    #ifdef _BH_LINUX
-        #define DIR_SEPARATOR '/'
-    #endif
-    #ifdef _BH_WINDOWS
-        #define DIR_SEPARATOR '\\'
-    #endif
-
-    char *library = name;
-    fori (i, 0, (i32) strlen(name)) {
-        if (name[i] == DIR_SEPARATOR) library = &name[i + 1];
-    }
-
-    char *library_load_name_tmp = bh_bprintf("onyx_library_%s", library);
-    char *library_load_name = alloca(strlen(library_load_name_tmp) + 1);
-    strcpy(library_load_name, library_load_name_tmp);
-    LibraryLinker library_load;
-
-    #ifdef _BH_LINUX
-    char *library_name = bh_lookup_file(name, ".", ".so", 1, (const char **) ctx->library_paths, 1);
-    void* handle = dlopen(library_name, RTLD_LAZY);
-    if (handle == NULL) {
-        printf("ERROR LOADING LIBRARY %s: %s\n", name, dlerror());
-        return NULL;
-    }
-
-    library_load = (LibraryLinker) dlsym(handle, library_load_name);
-    if (library_load == NULL) {
-        printf("ERROR RESOLVING '%s': %s\n", library_load_name, dlerror());
-        return NULL;
-    }
-    #endif
-
-    #ifdef _BH_WINDOWS
-    char *library_name = bh_lookup_file(name, ".", ".dll", 1, (const char **) ctx->library_paths, 1);
-    HMODULE handle = LoadLibraryA(library_name);
-    if (handle == NULL) {
-        printf("ERROR LOADING LIBRARY %s: %d\n", name, GetLastError());
-        return NULL;
-    }
-
-    library_load = (LibraryLinker) GetProcAddress(handle, library_load_name);
-    if (library_load == NULL) {
-        printf("ERROR RESOLVING '%s': %d\n", library_load_name, GetLastError());
-        return NULL;
-    }
-    #endif
-
-    return library_load(runtime);
-}
-
-static void lookup_and_load_custom_libraries(LibraryLinkContext *ctx, bh_arr(WasmFuncDefinition **)* p_out) {
-    bh_arr(WasmFuncDefinition **) out = *p_out;
-
-    bh_buffer wasm_bytes = ctx->wasm_bytes;
-
-    i32 cursor = 8; // skip the magic number and version
-    while (cursor < wasm_bytes.length) {
-        u64 section_number = uleb128_to_uint(wasm_bytes.data, &cursor);
-        u64 section_size   = uleb128_to_uint(wasm_bytes.data, &cursor);
-
-        i32 section_start = cursor;
-        if (section_number == 0) {
-            u64 name_len = uleb128_to_uint(wasm_bytes.data, &cursor);
-            if (!strncmp(wasm_bytes.data + cursor, "_onyx_libs", name_len)) {
-                cursor += name_len;
-                u64 lib_count = uleb128_to_uint(wasm_bytes.data, &cursor);
-
-                fori (i, 0, (i64) lib_count) {
-                    u64 lib_path_length = uleb128_to_uint(wasm_bytes.data, &cursor);
-                    lib_path_length = bh_min(lib_path_length, 512);
-
-                    char *lib_path = malloc(lib_path_length);
-                    strncpy(lib_path, wasm_bytes.data + cursor, lib_path_length);
-                    lib_path[lib_path_length] = '\0';
-                    bh_path_convert_separators(lib_path);
-                    cursor += lib_path_length;
-
-                    bh_arr_push(ctx->library_paths, lib_path);
-                }
-
-                lib_count = uleb128_to_uint(wasm_bytes.data, &cursor);
-
-                fori (i, 0, (i64) lib_count) {
-                    u64 lib_name_length = uleb128_to_uint(wasm_bytes.data, &cursor);
-                    lib_name_length = bh_min(lib_name_length, 256);
-
-                    char library_name[256];
-                    strncpy(library_name, wasm_bytes.data + cursor, lib_name_length);
-                    library_name[lib_name_length] = '\0';
-                    cursor += lib_name_length;
-
-                    WasmFuncDefinition** lib = onyx_load_library(ctx, library_name);
-                    if (lib) {
-                        bh_arr_push(out, lib);
-                    }
-                }
-                break;
-            }
-        }
-
-        cursor = section_start + section_size;
-    }
-
-    *p_out = out;
-}
-
-static void onyx_print_trap(wasm_trap_t* trap) {
-    wasm_message_t msg;
-    wasm_trap_message(trap, &msg);
-    bh_printf("TRAP: %b\n", msg.data, msg.size);
-
-    i32 func_name_section = 0;
-
-    i32 cursor = 8; // skip the magic number and version
-    while (cursor < wasm_raw_bytes.length) {
-        u64 section_number = uleb128_to_uint(wasm_raw_bytes.data, &cursor);
-        u64 section_size   = uleb128_to_uint(wasm_raw_bytes.data, &cursor);
-
-        i32 section_start = cursor;
-        if (section_number == 0) {
-            u64 name_len = uleb128_to_uint(wasm_raw_bytes.data, &cursor);
-            if (!strncmp(wasm_raw_bytes.data + cursor, "_onyx_func_offsets", name_len)) {
-                cursor += name_len;
-                func_name_section = cursor;
-                break;
-            }
-        }
-
-        cursor = section_start + section_size;
-    }
-
-    if (func_name_section == 0) return;
-
-    bh_printf("TRACE:\n");
-    wasm_frame_vec_t frames;
-    wasm_trap_trace(trap, &frames);
-    fori (i, 0, (i32) frames.size) {
-        i32 func_idx   = wasm_frame_func_index(frames.data[i]);
-        i32 mod_offset = wasm_frame_module_offset(frames.data[i]);
-
-        i32 cursor = func_name_section + 4 * func_idx;
-        i32 func_offset = *(i32 *) (wasm_raw_bytes.data + cursor);
-        char* func_name = wasm_raw_bytes.data + func_name_section + func_offset;
-
-        bh_printf("    func[%d]:%p at %s\n", func_idx, mod_offset, func_name);
-    }
-}
-
-static void cleanup_wasm_objects() {
-    if (wasm_instance) wasm_instance_delete(wasm_instance);
-    if (wasm_module) wasm_module_delete(wasm_module);
-    if (wasm_store)  wasm_store_delete(wasm_store);
-    if (wasm_engine) wasm_engine_delete(wasm_engine);
-}
-
-//
-// This could be cleaned up a bit, as this function directly modifies various global variables.
-// Those being wasm_memory and wasm_imports.
-static b32 link_wasm_imports(bh_arr(WasmFuncDefinition **) linkable_functions, wasm_module_t *wasm_module) {
-    wasm_importtype_vec_t module_imports;
-    wasm_module_imports(wasm_module, &module_imports);
-
-    wasm_extern_vec_new_uninitialized(&wasm_imports, module_imports.size); // @Free
-
-    fori (i, 0, (i32) module_imports.size) {
-        const wasm_name_t* module_name = wasm_importtype_module(module_imports.data[i]);
-        const wasm_name_t* import_name = wasm_importtype_name(module_imports.data[i]);
-
-        wasm_extern_t* import = NULL;
-
-        if (wasm_name_equals_string(module_name, "onyx")) {
-            if (wasm_name_equals_string(import_name, "memory")) {
-                if (wasm_memory == NULL) {
-                    wasm_limits_t limits = { 1024, 65536 };
-                    wasm_memorytype_t* memory_type = wasm_memorytype_new(&limits);
-                    wasm_memory = wasm_memory_new(wasm_store, memory_type);
-                }
-
-                import = wasm_memory_as_extern(wasm_memory);
-                goto import_found;
-            }
-        }
-
-        bh_arr_each(WasmFuncDefinition **, library_funcs, linkable_functions) {
-            WasmFuncDefinition **pcurrent_function = *library_funcs;
-            while (*pcurrent_function != NULL) {
-                WasmFuncDefinition *cf = *pcurrent_function;
-                if (wasm_name_equals_string(module_name, cf->module_name) && wasm_name_equals_string(import_name, cf->import_name)) {
-                    wasm_valtype_vec_t wasm_params;
-                    wasm_valtype_vec_new_uninitialized(&wasm_params, cf->params->count);
-                    fori (k, 0, cf->params->count) wasm_params.data[k] = wasm_valtype_new(cf->params->types[k]);
-
-                    wasm_valtype_vec_t wasm_results;
-                    wasm_valtype_vec_new_uninitialized(&wasm_results, cf->results->count);
-                    fori (k, 0, cf->results->count) wasm_results.data[k] = wasm_valtype_new(cf->results->types[k]);
-
-                    wasm_functype_t* wasm_functype = wasm_functype_new(&wasm_params, &wasm_results);
-
-                    wasm_func_t* wasm_func = wasm_func_new(wasm_store, wasm_functype, cf->func);
-                    import = wasm_func_as_extern(wasm_func);
-                    goto import_found;
-                }
-
-                pcurrent_function += 1;
-            }
-        }
-
-        goto bad_import;
-
-    import_found:
-        wasm_imports.data[i] = import;
-        continue;
-
-
-    bad_import:
-        bh_printf("Couldn't find import %b.%b.\n", module_name->data, module_name->size, import_name->data, import_name->size);
-        return 0;
-    }
-
-    return 1;
-}
-
-void onyx_run_initialize(b32 debug_enabled) {
-    wasm_config = wasm_config_new();
-    if (!wasm_config) {
-        cleanup_wasm_objects();
-        return;
-    }
-
-#ifdef USE_OVM_DEBUGGER
-    void wasm_config_enable_debug(wasm_config_t *config, int value);
-    wasm_config_enable_debug(wasm_config, debug_enabled);
-#endif
-
-#ifndef USE_OVM_DEBUGGER
-    if (debug_enabled) {
-        printf("Warning: --debug does nothing if libovmwasm.so is not being used!\n");
-    }
-
-    // Prefer the LLVM compile because it is faster. This should be configurable from the command line and/or a top-level directive.
-    if (wasmer_is_compiler_available(LLVM)) {
-        wasm_config_set_compiler(wasm_config, LLVM);
-    }
-
-    wasmer_features_t* features = wasmer_features_new();
-    wasmer_features_simd(features, 1);
-    wasmer_features_threads(features, 1);
-    wasmer_features_bulk_memory(features, 1);
-    wasm_config_set_features(wasm_config, features);
-#endif
-
-    wasm_engine = wasm_engine_new_with_config(wasm_config);
-    if (!wasm_engine) {
-        cleanup_wasm_objects();
-        return;
-    }
-
-    wasm_store  = wasm_store_new(wasm_engine);
-    if (!wasm_store) {
-        cleanup_wasm_objects();
-        return;
-    }
-
-    // See comment in onyx_library.h about us being the linker.
-    wasm_runtime.wasm_memory_data = &wasm_memory_data;
-    wasm_runtime.wasm_extern_lookup_by_name = &wasm_extern_lookup_by_name;
-    wasm_runtime.wasm_extern_as_func = &wasm_extern_as_func;
-    wasm_runtime.wasm_func_call = &wasm_func_call;
-    wasm_runtime.wasm_instance_new = &wasm_instance_new;
-    wasm_runtime.wasm_store_new = &wasm_store_new;
-    wasm_runtime.wasm_store_delete = &wasm_store_delete;
-    wasm_runtime.onyx_print_trap = &onyx_print_trap;
-}
-
-b32 onyx_run_wasm(bh_buffer wasm_bytes, int argc, char *argv[]) {
-    runtime = &wasm_runtime;
-
-    if (strncmp(wasm_bytes.data, "ONYX", 4)) {
-        printf("Bad magic bytes for Onyx binary.\n");
-        return 0;
-    }
-
-    wasm_bytes.data[0] = '\0';
-    wasm_bytes.data[1] = 'a';
-    wasm_bytes.data[2] = 's';
-    wasm_bytes.data[3] = 'm';
-    wasm_raw_bytes = wasm_bytes;
-
-    bh_arr(WasmFuncDefinition **) linkable_functions = NULL;
-    bh_arr_new(bh_heap_allocator(), linkable_functions, 4);
-
-    {
-        LibraryLinkContext lib_ctx;
-        lib_ctx.wasm_bytes = wasm_bytes;
-        lib_ctx.library_paths = NULL;
-        lookup_and_load_custom_libraries(&lib_ctx, &linkable_functions);
-
-        bh_arr_free(lib_ctx.library_paths);
-    }
-
-    wasm_byte_vec_t wasm_data;
-    wasm_data.size = wasm_bytes.length;
-    wasm_data.data = wasm_bytes.data;
-
-    wasm_module = wasm_module_new(wasm_store, &wasm_data);
-    if (!wasm_module) {
-        cleanup_wasm_objects();
-        return 0;
-    }
-
-    wasm_imports = (wasm_extern_vec_t) WASM_EMPTY_VEC;
-    if (!link_wasm_imports(linkable_functions, wasm_module)) {
-        return 0;
-    }
-
-    wasm_trap_t* traps = NULL;
-
-    wasm_instance = wasm_instance_new(wasm_store, wasm_module, &wasm_imports, &traps);
-    if (!wasm_instance) {
-        cleanup_wasm_objects();
-        return 0;
-    }
-
-    wasm_runtime.wasm_engine = wasm_engine;
-    wasm_runtime.wasm_module = wasm_module;
-    wasm_runtime.wasm_imports = wasm_imports;
-    wasm_runtime.wasm_memory = wasm_memory;
-    wasm_runtime.wasm_instance = wasm_instance;
-    
-    wasm_runtime.argc = argc;
-    wasm_runtime.argv = argv;
-
-    wasm_extern_t* start_extern = wasm_extern_lookup_by_name(wasm_module, wasm_instance, "_start");
-    wasm_func_t*   start_func   = wasm_extern_as_func(start_extern);
-
-    wasm_val_vec_t args;
-    wasm_val_vec_t results;
-    wasm_val_vec_new_uninitialized(&args, 0);
-    wasm_val_vec_new_uninitialized(&results, 1);
-
-    wasm_trap_t *run_trap = wasm_func_call(start_func, &args, &results);
-
-#if 1
-    if (run_trap != NULL) onyx_print_trap(run_trap);
-#endif
-
-    goto end;
-
-    bh_printf("An error occured trying to run the WASM module...\n");
-
-#ifndef USE_OVM_DEBUGGER
-    i32 len = wasmer_last_error_length();
-    char *buf = alloca(len + 1);
-    wasmer_last_error_message(buf, len);
-    bh_printf("%b\n", buf, len);
-#endif
-
-end:
-    cleanup_wasm_objects();
-    return run_trap == NULL;
-}
diff --git a/src/wasm_type_table.h b/src/wasm_type_table.h
deleted file mode 100644 (file)
index 2cb1c67..0000000
+++ /dev/null
@@ -1,902 +0,0 @@
-// This file is directly included in src/onxywasm.c
-// It is here purely to decrease the amount of clutter in the main file.
-
-typedef struct StructMethodData {
-    u32 name_loc;
-    u32 name_len;
-    u32 type;
-    u32 data_loc;
-} StructMethodData;
-
-static u64 build_type_table(OnyxWasmModule* module) {
-
-    bh_arr(u32) base_patch_locations=NULL;
-    bh_arr_new(global_heap_allocator, base_patch_locations, 256);
-
-#define PATCH (bh_arr_push(base_patch_locations, table_buffer.length))
-#define WRITE_PTR(val) \
-    bh_buffer_align(&table_buffer, POINTER_SIZE); \
-    PATCH; \
-    if (POINTER_SIZE == 4) bh_buffer_write_u32(&table_buffer, val); \
-    if (POINTER_SIZE == 8) bh_buffer_write_u64(&table_buffer, val); 
-#define WRITE_SLICE(ptr, count) \
-    WRITE_PTR(ptr); \
-    if (POINTER_SIZE == 4) bh_buffer_write_u32(&table_buffer, count); \
-    if (POINTER_SIZE == 8) bh_buffer_write_u64(&table_buffer, count); 
-
-    // This is the data behind the "type_table" slice in runtime/info/types.onyx
-    #if (POINTER_SIZE == 4)
-        #define Table_Info_Type u32
-    #else
-        #define Table_Info_Type u64
-    #endif
-    u32 type_count = bh_arr_length(type_map.entries) + 1;
-    Table_Info_Type* table_info = bh_alloc_array(global_heap_allocator, Table_Info_Type, type_count); // HACK
-    memset(table_info, 0, type_count * sizeof(Table_Info_Type));
-
-    bh_buffer table_buffer;
-    bh_buffer_init(&table_buffer, global_heap_allocator, 4096);
-
-    u32 type_table_info_data_id = NEXT_DATA_ID(module);
-
-    ConstExprContext constexpr_ctx;
-    constexpr_ctx.module = module;
-    constexpr_ctx.data_id = type_table_info_data_id;
-
-    // Write a "NULL" at the beginning so nothing will have to point to the first byte of the buffer.
-    bh_buffer_write_u64(&table_buffer, 0);
-
-    bh_arr_each(bh__imap_entry, type_entry, type_map.entries) {
-        u64 type_idx = type_entry->key;
-        Type* type = (Type *) type_entry->value;
-
-        switch (type->kind) {
-            case Type_Kind_Basic: {
-                table_info[type_idx] = table_buffer.length;
-                bh_buffer_write_u32(&table_buffer, type->kind);
-                bh_buffer_write_u32(&table_buffer, type_size_of(type));
-                bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
-                bh_buffer_write_u32(&table_buffer, type->Basic.kind);
-                break;
-            }
-
-            case Type_Kind_Pointer: {
-                table_info[type_idx] = table_buffer.length;
-                bh_buffer_write_u32(&table_buffer, type->kind);
-                bh_buffer_write_u32(&table_buffer, type_size_of(type));
-                bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
-                bh_buffer_write_u32(&table_buffer, type->Pointer.elem->id);
-                break;
-            }
-
-            case Type_Kind_Array: {
-                table_info[type_idx] = table_buffer.length;
-                bh_buffer_write_u32(&table_buffer, type->kind);
-                bh_buffer_write_u32(&table_buffer, type_size_of(type));
-                bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
-                bh_buffer_write_u32(&table_buffer, type->Array.elem->id);
-                bh_buffer_write_u32(&table_buffer, type->Array.count);
-                break;
-            }
-
-            case Type_Kind_Slice: {
-                table_info[type_idx] = table_buffer.length;
-                bh_buffer_write_u32(&table_buffer, type->kind);
-                bh_buffer_write_u32(&table_buffer, type_size_of(type));
-                bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
-                bh_buffer_write_u32(&table_buffer, type->Slice.elem->id);
-                break;
-            }
-
-            case Type_Kind_DynArray: {
-                table_info[type_idx] = table_buffer.length;
-                bh_buffer_write_u32(&table_buffer, type->kind);
-                bh_buffer_write_u32(&table_buffer, type_size_of(type));
-                bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
-                bh_buffer_write_u32(&table_buffer, type->DynArray.elem->id);
-                break;
-            }
-
-            case Type_Kind_VarArgs: {
-                table_info[type_idx] = table_buffer.length;
-                bh_buffer_write_u32(&table_buffer, type->kind);
-                bh_buffer_write_u32(&table_buffer, type_size_of(type));
-                bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
-                bh_buffer_write_u32(&table_buffer, type->VarArgs.elem->id);
-                break;
-            }
-
-            case Type_Kind_Compound: {
-                u32 components_base = table_buffer.length;
-
-                u32 components_count = type->Compound.count;
-                fori (i, 0, components_count) {
-                    u32 type_idx = type->Compound.types[i]->id;
-                    bh_buffer_write_u32(&table_buffer, type_idx);
-                }
-
-                bh_buffer_align(&table_buffer, 8);
-                table_info[type_idx] = table_buffer.length;
-                bh_buffer_write_u32(&table_buffer, type->kind);
-                bh_buffer_write_u32(&table_buffer, type_size_of(type));
-                bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
-                WRITE_SLICE(components_base, components_count);
-                break;
-            }
-
-            case Type_Kind_Function: {
-                u32 parameters_base = table_buffer.length;
-
-                u32 parameters_count = type->Function.param_count;
-                fori (i, 0, parameters_count) {
-                    u32 type_idx = type->Function.params[i]->id;
-                    bh_buffer_write_u32(&table_buffer, type_idx);
-                }
-
-                table_info[type_idx] = table_buffer.length;
-                bh_buffer_write_u32(&table_buffer, type->kind);
-                bh_buffer_write_u32(&table_buffer, type_size_of(type));
-                bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
-                bh_buffer_write_u32(&table_buffer, type->Function.return_type->id);
-
-                WRITE_SLICE(parameters_base, parameters_count);
-
-                bh_buffer_write_u32(&table_buffer, type->Function.vararg_arg_pos > 0 ? 1 : 0);
-                break;
-            }
-
-            case Type_Kind_Enum: {
-                AstEnumType* ast_enum = (AstEnumType *) type->ast_type;
-                u32 member_count = bh_arr_length(ast_enum->values);
-                u32* name_locations = bh_alloc_array(global_scratch_allocator, u32, member_count);
-
-                u32 i = 0;
-                bh_arr_each(AstEnumValue *, value, ast_enum->values) {
-                    name_locations[i++] = table_buffer.length;
-
-                    bh_buffer_append(&table_buffer, (*value)->token->text, (*value)->token->length);
-                }
-                bh_buffer_align(&table_buffer, 8);
-
-                u32 member_base = table_buffer.length;
-                i = 0;
-                bh_arr_each(AstEnumValue *, value, ast_enum->values) {
-                    u32 name_loc = name_locations[i++];
-
-                    bh_buffer_align(&table_buffer, 8);
-                    WRITE_SLICE(name_loc, (*value)->token->length);
-
-                    assert((*value)->value->kind == Ast_Kind_NumLit);
-                    AstNumLit *num = (AstNumLit *) (*value)->value;
-                    bh_buffer_write_u64(&table_buffer, num->value.l);
-                }
-
-                u32 name_base = table_buffer.length;
-                u32 name_length = strlen(type->Enum.name);
-                bh_buffer_append(&table_buffer, type->Enum.name, name_length);
-                bh_buffer_align(&table_buffer, 8);
-
-                table_info[type_idx] = table_buffer.length;
-                bh_buffer_write_u32(&table_buffer, type->kind);
-                bh_buffer_write_u32(&table_buffer, type_size_of(type));
-                bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
-                bh_buffer_write_u32(&table_buffer, type->Enum.backing->id);
-                WRITE_SLICE(name_base, name_length);
-                WRITE_SLICE(member_base, member_count);
-                bh_buffer_write_u32(&table_buffer, type->Enum.is_flags ? 1 : 0);
-                break;
-            }
-
-            case Type_Kind_Struct: {
-                TypeStruct* s = &type->Struct;
-                u32* name_locations = bh_alloc_array(global_scratch_allocator, u32, s->mem_count);
-                u32* param_locations = bh_alloc_array(global_scratch_allocator, u32, bh_arr_length(s->poly_sln));
-                u32* value_locations = bh_alloc_array(global_scratch_allocator, u32, s->mem_count);
-                u32* meta_locations = bh_alloc_array(global_scratch_allocator, u32, s->mem_count);
-                u32* struct_tag_locations = bh_alloc_array(global_scratch_allocator, u32, bh_arr_length(s->meta_tags));
-                memset(value_locations, 0, s->mem_count * sizeof(u32));
-                memset(meta_locations, 0, s->mem_count * sizeof(u32));
-                memset(struct_tag_locations, 0, bh_arr_length(s->meta_tags) * sizeof(u32));
-
-                // Member names
-                u32 i = 0;
-                bh_arr_each(StructMember*, pmem, s->memarr) {
-                    StructMember* mem = *pmem;
-
-                    name_locations[i++] = table_buffer.length;
-                    bh_buffer_append(&table_buffer, mem->name, strlen(mem->name));
-                }
-
-                bh_buffer_align(&table_buffer, 8);
-
-                // Polymorphic solutions
-                i = 0;
-                bh_arr_each(AstPolySolution, sln, s->poly_sln) {
-                    bh_buffer_align(&table_buffer, 8);
-                    param_locations[i++] = table_buffer.length;
-
-                    switch (sln->kind) {
-                        case PSK_Type: {
-                            // NOTE: This assumes a little endian compiler (which is assumed in other part of the code too)
-                            bh_buffer_append(&table_buffer, &sln->type->id, 4);
-                            break;
-                        }
-
-                        case PSK_Value: {
-                            assert(sln->value->type);
-                            u32 size = type_size_of(sln->value->type);
-
-                            bh_buffer_grow(&table_buffer, table_buffer.length + size);
-                            constexpr_ctx.data = table_buffer.data;
-                            if (emit_constexpr_(&constexpr_ctx, sln->value, table_buffer.length)) {
-                                table_buffer.length += size;
-                                break;
-                            }
-
-                            // fallthrough
-                        }
-
-                        default: {
-                            // Set to null if this is not known how to encode
-                            param_locations[i-1] = 0;
-                            break;
-                        }
-                    }
-                }
-
-                bh_buffer_align(&table_buffer, 8);
-
-                // Member default values
-                i = 0;
-                bh_arr_each(StructMember*, pmem, s->memarr) {
-                    StructMember* mem = *pmem;
-
-                    if (mem->initial_value == NULL || *mem->initial_value == NULL) {
-                        i++;
-                        continue;
-                    }
-
-                    AstTyped* value = *mem->initial_value;
-                    assert(value->type);
-
-                    if ((value->flags & Ast_Flag_Comptime) == 0) {
-                        // onyx_report_warning(value->token->pos, "Warning: skipping generating default value for '%s' in '%s' because it is not compile-time known.\n", mem->name, s->name);
-                        i++;
-                        continue;
-                    }
-
-                    u32 size = type_size_of(value->type);
-                    bh_buffer_align(&table_buffer, type_alignment_of(value->type));
-
-                    bh_buffer_grow(&table_buffer, table_buffer.length + size);
-                    constexpr_ctx.data = table_buffer.data;
-                    if (!emit_constexpr_(&constexpr_ctx, value, table_buffer.length)) {
-                        // Failed to generate raw data
-                        // onyx_report_warning(value->token->pos, "Warning: failed to generate default value for '%s' in '%s'.\n", mem->name, s->name);
-                        value_locations[i++] = 0;
-
-                    } else {
-                        // Success 
-                        value_locations[i++] = table_buffer.length;
-                        table_buffer.length += size;
-                    }
-                }
-
-                // Member tags
-                i = 0;
-                bh_arr_each(StructMember*, pmem, s->memarr) {
-                    StructMember* mem = *pmem;
-
-                    if (mem->meta_tags == NULL) {
-                        i += 1;
-                        continue;
-                    }
-
-                    bh_arr(AstTyped *) meta_tags = mem->meta_tags;
-                    assert(meta_tags);
-
-                    bh_arr(u64) meta_tag_locations=NULL;
-                    bh_arr_new(global_heap_allocator, meta_tag_locations, bh_arr_length(meta_tags));
-
-                    int j = 0;
-                    bh_arr_each(AstTyped *, meta, meta_tags) {
-                        AstTyped* value = *meta;                        
-                        assert(value->flags & Ast_Flag_Comptime);
-                        assert(value->type);
-
-                        u32 size = type_size_of(value->type);
-                        bh_buffer_align(&table_buffer, type_alignment_of(value->type));
-                        meta_tag_locations[j] = table_buffer.length;
-
-                        bh_buffer_grow(&table_buffer, table_buffer.length + size);
-                        constexpr_ctx.data = table_buffer.data;
-                        assert(emit_constexpr_(&constexpr_ctx, value, table_buffer.length));
-                        table_buffer.length += size;
-
-                        j += 1;
-                    }
-
-                    bh_buffer_align(&table_buffer, 8);
-                    meta_locations[i] = table_buffer.length;
-
-                    fori (k, 0, bh_arr_length(meta_tags)) {
-                        WRITE_SLICE(meta_tag_locations[k], meta_tags[k]->type->id);
-                    }
-
-                    bh_arr_free(meta_tag_locations);
-                    i += 1;
-                }
-
-                bh_buffer_align(&table_buffer, 8);
-                u32 members_base = table_buffer.length;
-
-                // Member array
-                i = 0;
-                bh_arr_each(StructMember*, pmem, s->memarr) {
-                    StructMember* mem = *pmem;
-
-                    u32 name_loc = name_locations[i];
-                    u32 value_loc = value_locations[i];
-                    u32 meta_loc = meta_locations[i++];
-
-                    WRITE_SLICE(name_loc, strlen(mem->name));
-                    bh_buffer_write_u32(&table_buffer, mem->offset);
-                    bh_buffer_write_u32(&table_buffer, mem->type->id);
-                    bh_buffer_write_byte(&table_buffer, mem->used ? 1 : 0);
-                    
-                    WRITE_PTR(value_loc);
-
-                    WRITE_SLICE(meta_loc, bh_arr_length(mem->meta_tags));
-                }
-
-                bh_buffer_align(&table_buffer, 8);
-                u32 params_base = table_buffer.length;
-
-                // Polymorphic solution any array
-                i = 0;
-                bh_arr_each(AstPolySolution, sln, s->poly_sln) {
-                    WRITE_PTR(param_locations[i++]);
-
-                    if (sln->kind == PSK_Type) bh_buffer_write_u32(&table_buffer, basic_types[Basic_Kind_Type_Index].id);
-                    else                       bh_buffer_write_u32(&table_buffer, sln->value->type->id);
-                }
-
-                // Struct tag array
-                i = 0;
-                bh_arr_each(AstTyped *, tag, s->meta_tags) {
-                    AstTyped* value = *tag;                        
-                    assert(value->flags & Ast_Flag_Comptime);
-                    assert(value->type);
-
-                    u32 size = type_size_of(value->type);
-                    bh_buffer_align(&table_buffer, type_alignment_of(value->type));
-                    struct_tag_locations[i] = table_buffer.length;
-
-                    bh_buffer_grow(&table_buffer, table_buffer.length + size);
-                    constexpr_ctx.data = table_buffer.data;
-                    assert(emit_constexpr_(&constexpr_ctx, value, table_buffer.length));
-                    table_buffer.length += size;
-
-                    i += 1;
-                }
-
-                // Struct methods
-                bh_arr(StructMethodData) method_data=NULL;
-                AstType *ast_type = type->ast_type;
-                if (ast_type && ast_type->kind == Ast_Kind_Struct_Type) {
-                    AstStructType *struct_type  = (AstStructType *) ast_type;
-                    Scope*         struct_scope = struct_type->scope;
-
-                    if (struct_scope == NULL) goto no_methods;
-
-                    fori (i, 0, shlen(struct_scope->symbols)) {
-                        AstFunction* node = (AstFunction *) strip_aliases(struct_scope->symbols[i].value);
-                        if (node->kind != Ast_Kind_Function) continue;
-                        assert(node->entity);
-                        assert(node->entity->function == node);
-
-                        // Name
-                        char *name = struct_scope->symbols[i].key;
-                        u32 name_loc = table_buffer.length;
-                        u32 name_len = strlen(name);
-                        bh_buffer_append(&table_buffer, name, name_len);
-
-                        // any data member
-                        bh_buffer_align(&table_buffer, 4);
-                        u32 data_loc = table_buffer.length;
-                        u32 func_idx = get_element_idx(module, node);
-                        bh_buffer_write_u32(&table_buffer, func_idx);
-                        
-                        bh_arr_push(method_data, ((StructMethodData) {
-                            .name_loc = name_loc,
-                            .name_len = name_len,
-                            .type     = node->type->id,
-                            .data_loc = data_loc,
-                        }));
-                    }
-                }
-
-                no_methods:
-
-                bh_buffer_align(&table_buffer, 4);
-                u32 method_data_base = table_buffer.length;
-
-                i = 0;
-                bh_arr_each(StructMethodData, method, method_data) {
-                    WRITE_SLICE(method->name_loc, method->name_len);
-                    WRITE_PTR(method->data_loc); 
-                    bh_buffer_write_u32(&table_buffer, method->type);
-                }
-
-                bh_buffer_align(&table_buffer, 8);
-                u32 struct_tag_base = table_buffer.length;
-
-                fori (i, 0, bh_arr_length(s->meta_tags)) {
-                    WRITE_SLICE(struct_tag_locations[i], s->meta_tags[i]->type->id);
-                }
-
-                // Struct name
-                u32 name_base = 0;
-                u32 name_length = 0;
-                if (s->name) {
-                    name_length = strlen(s->name);
-                    name_base = table_buffer.length;
-                    bh_buffer_append(&table_buffer, s->name, name_length);
-                }
-
-                bh_buffer_align(&table_buffer, 8);
-                table_info[type_idx] = table_buffer.length;
-                bh_buffer_write_u32(&table_buffer, type->kind);
-                bh_buffer_write_u32(&table_buffer, type_size_of(type));
-                bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
-
-                if (type->Struct.constructed_from != NULL) {
-                    bh_buffer_write_u32(&table_buffer, type->Struct.constructed_from->type_id);
-                } else {
-                    bh_buffer_write_u32(&table_buffer, 0);
-                }
-
-                WRITE_SLICE(name_base, name_length);
-                WRITE_SLICE(members_base, s->mem_count);
-                WRITE_SLICE(params_base, bh_arr_length(s->poly_sln));
-                WRITE_SLICE(struct_tag_base, bh_arr_length(s->meta_tags));
-                WRITE_SLICE(method_data_base, bh_arr_length(method_data));
-
-                bh_arr_free(method_data);
-                break;
-            }
-
-            case Type_Kind_PolyStruct: {
-                u32* tag_locations = bh_alloc_array(global_scratch_allocator, u32, bh_arr_length(type->PolyStruct.meta_tags));
-                memset(tag_locations, 0, sizeof(u32) * bh_arr_length(type->PolyStruct.meta_tags));
-
-                u32 name_base = table_buffer.length;
-                u32 name_length = strlen(type->PolyStruct.name);
-                bh_buffer_append(&table_buffer, type->PolyStruct.name, name_length);
-
-                u32 tags_count = bh_arr_length(type->PolyStruct.meta_tags);
-                i32 i = 0;
-                bh_arr_each(AstTyped *, tag, type->PolyStruct.meta_tags) {
-                    AstTyped* value = *tag;                        
-
-                    // Polymorphic structs are weird in this case, because the tag might not be constructed generically for
-                    // the polymorphic structure so it should only be constructed for actual solidified structures.
-                    // See core/containers/map.onyx with Custom_Format for an example.
-                    if (!(value->flags & Ast_Flag_Comptime)) {
-                        tags_count--;
-                        continue;
-                    }
-
-                    assert(value->type);
-
-                    u32 size = type_size_of(value->type);
-                    bh_buffer_align(&table_buffer, type_alignment_of(value->type));
-                    tag_locations[i] = table_buffer.length;
-
-                    bh_buffer_grow(&table_buffer, table_buffer.length + size);
-
-                    constexpr_ctx.data = table_buffer.data;
-                    assert(emit_constexpr_(&constexpr_ctx, value, table_buffer.length));
-                    table_buffer.length += size;
-
-                    i += 1;
-                }
-
-                bh_buffer_align(&table_buffer, 8);
-                u32 tags_base = table_buffer.length;
-
-                fori (i, 0, tags_count) {
-                    WRITE_SLICE(tag_locations[i], type->PolyStruct.meta_tags[i]->type->id);
-                }
-
-                bh_buffer_align(&table_buffer, 8);
-                table_info[type_idx] = table_buffer.length;
-                bh_buffer_write_u32(&table_buffer, type->kind);
-                bh_buffer_write_u32(&table_buffer, 0);
-                bh_buffer_write_u32(&table_buffer, 0);
-                WRITE_SLICE(name_base, name_length);
-                WRITE_SLICE(tags_base, tags_count);
-
-                break;
-            }
-        
-            case Type_Kind_Distinct: {
-                u32 name_base = table_buffer.length;
-                u32 name_length = strlen(type->Distinct.name);
-                bh_buffer_append(&table_buffer, type->Distinct.name, name_length);
-                bh_buffer_align(&table_buffer, 8);
-
-                table_info[type_idx] = table_buffer.length;
-                bh_buffer_write_u32(&table_buffer, type->kind);
-                bh_buffer_write_u32(&table_buffer, type_size_of(type));
-                bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
-                bh_buffer_write_u32(&table_buffer, type->Distinct.base_type->id);
-                WRITE_SLICE(name_base, name_length);
-                break;
-            }
-        }
-    }
-
-    if (context.options->verbose_output == 1) {
-        bh_printf("Type table size: %d bytes.\n", table_buffer.length);
-    }
-
-    WasmDatum type_info_data = {
-        .alignment = 8,
-        .length = table_buffer.length,
-        .data = table_buffer.data,
-    };
-    emit_data_entry(module, &type_info_data);
-    assert(type_info_data.id == type_table_info_data_id);
-
-    bh_arr_each(u32, patch_loc, base_patch_locations) {
-        DatumPatchInfo patch;
-        patch.kind = Datum_Patch_Relative;
-        patch.data_id = type_info_data.id;
-        patch.offset = 0;
-        patch.index = type_info_data.id;
-        patch.location = *patch_loc;
-        bh_arr_push(module->data_patches, patch);
-    }
-
-    WasmDatum type_table_data = {
-        .alignment = POINTER_SIZE,
-        .length = type_count * POINTER_SIZE,
-        .data = table_info,
-    };
-    emit_data_entry(module, &type_table_data);
-
-    fori (i, 0, type_count) {
-        DatumPatchInfo patch;
-        patch.kind = Datum_Patch_Data;
-        patch.data_id = type_info_data.id;
-        patch.offset = table_info[i];
-        patch.index = type_table_data.id;
-        patch.location = i * POINTER_SIZE;
-        bh_arr_push(module->data_patches, patch);
-    }
-
-    Table_Info_Type* tmp_data = bh_alloc(global_heap_allocator, 2 * POINTER_SIZE);
-    tmp_data[0] = 0;
-    tmp_data[1] = type_count;
-    WasmDatum type_table_global_data = {
-        .alignment = POINTER_SIZE,
-        .length = 2 * POINTER_SIZE,
-        .data = tmp_data,
-    };
-    emit_data_entry(module, &type_table_global_data);
-
-    {
-        DatumPatchInfo patch;
-        patch.kind = Datum_Patch_Data;
-        patch.data_id = type_table_data.id;
-        patch.offset = 0;
-        patch.index = type_table_global_data.id;
-        patch.location = 0;
-        bh_arr_push(module->data_patches, patch);
-    }
-
-    return type_table_global_data.id;
-
-#undef WRITE_SLICE
-#undef WRITE_PTR
-#undef PATCH
-}
-
-
-
-static u64 build_foreign_blocks(OnyxWasmModule* module) {
-    bh_arr(u32) base_patch_locations=NULL;
-    bh_arr_new(global_heap_allocator, base_patch_locations, 256);
-
-#define PATCH (bh_arr_push(base_patch_locations, foreign_buffer.length))
-#define WRITE_PTR(val) \
-    bh_buffer_align(&foreign_buffer, POINTER_SIZE); \
-    PATCH; \
-    if (POINTER_SIZE == 4) bh_buffer_write_u32(&foreign_buffer, val); \
-    if (POINTER_SIZE == 8) bh_buffer_write_u64(&foreign_buffer, val); 
-#define WRITE_SLICE(ptr, count) \
-    WRITE_PTR(ptr); \
-    if (POINTER_SIZE == 4) bh_buffer_write_u32(&foreign_buffer, count); \
-    if (POINTER_SIZE == 8) bh_buffer_write_u64(&foreign_buffer, count); 
-
-    // This is the data behind the "type_table" slice in type_info.onyx
-    #if (POINTER_SIZE == 4)
-        #define Foreign_Block_Type u32
-    #else
-        #define Foreign_Block_Type u64
-    #endif
-    u32 block_count = bh_arr_length(module->foreign_blocks);
-    Foreign_Block_Type* foreign_info = bh_alloc_array(global_heap_allocator, Foreign_Block_Type, block_count); // HACK
-    memset(foreign_info, 0, block_count * sizeof(Foreign_Block_Type));
-
-    bh_buffer foreign_buffer;
-    bh_buffer_init(&foreign_buffer, global_heap_allocator, 4096);
-
-    // 
-    // This is necessary because 0 is an invalid offset to store in this
-    // buffer, as 0 will map to NULL. This could be a single byte insertion,
-    // but 64 bytes keeps better alignment.
-    bh_buffer_write_u64(&foreign_buffer, 0);
-
-    u32 index = 0;
-    bh_arr_each(AstForeignBlock *, pfb, module->foreign_blocks) {
-        AstForeignBlock *fb = *pfb;
-
-        u32 funcs_length = 0;
-
-        u32 *name_offsets = bh_alloc_array(global_scratch_allocator, u32, shlen(fb->scope->symbols));
-        u32 *name_lengths = bh_alloc_array(global_scratch_allocator, u32, shlen(fb->scope->symbols));
-        u32 *func_types   = bh_alloc_array(global_scratch_allocator, u32, shlen(fb->scope->symbols));
-
-        fori (i, 0, shlen(fb->scope->symbols)) {
-            AstFunction *func = (AstFunction *) fb->scope->symbols[i].value;
-            if (func->kind != Ast_Kind_Function) continue;
-
-            u32 func_name_base = foreign_buffer.length;
-            u32 func_name_length = func->foreign_name->length;
-            bh_buffer_append(&foreign_buffer, func->foreign_name->text, func_name_length);
-
-            name_offsets[funcs_length] = func_name_base;
-            name_lengths[funcs_length] = func_name_length;
-            func_types[funcs_length]   = func->type->id;
-            funcs_length++;
-        }
-
-        bh_buffer_align(&foreign_buffer, 8);
-        u32 funcs_base = foreign_buffer.length;
-
-        fori (i, 0, (i64) funcs_length) {
-            bh_buffer_align(&foreign_buffer, POINTER_SIZE);
-            WRITE_SLICE(name_offsets[i], name_lengths[i]);
-            bh_buffer_write_u32(&foreign_buffer, func_types[i]);
-        }
-
-        u32 name_base = foreign_buffer.length;
-        u32 name_length = fb->module_name->length;
-        bh_buffer_append(&foreign_buffer, fb->module_name->text, name_length);
-        bh_buffer_align(&foreign_buffer, 8);
-
-        foreign_info[index] = foreign_buffer.length;
-        WRITE_SLICE(name_base, name_length);
-        WRITE_SLICE(funcs_base, funcs_length);
-        index++;
-    }
-
-
-    if (context.options->verbose_output == 1) {
-        bh_printf("Foreign blocks size: %d bytes.\n", foreign_buffer.length);
-    }
-
-    WasmDatum foreign_info_data = {
-        .alignment = 8,
-        .length = foreign_buffer.length,
-        .data = foreign_buffer.data,
-    };
-    emit_data_entry(module, &foreign_info_data);
-
-    bh_arr_each(u32, patch_loc, base_patch_locations) {
-        DatumPatchInfo patch;
-        patch.kind = Datum_Patch_Relative;
-        patch.data_id = foreign_info_data.id;
-        patch.offset = 0;
-        patch.index = foreign_info_data.id;
-        patch.location = *patch_loc;
-        bh_arr_push(module->data_patches, patch);
-    }
-
-    WasmDatum foreign_table_data = {
-        .alignment = POINTER_SIZE,
-        .length = block_count * POINTER_SIZE,
-        .data = foreign_info,
-    };
-    emit_data_entry(module, &foreign_table_data);
-
-    fori (i, 0, block_count) {
-        DatumPatchInfo patch;
-        patch.kind = Datum_Patch_Data;
-        patch.data_id = foreign_info_data.id;
-        patch.offset = foreign_info[i];
-        patch.index = foreign_table_data.id;
-        patch.location = i * POINTER_SIZE;
-        bh_arr_push(module->data_patches, patch);
-    }
-
-    Foreign_Block_Type* tmp_data = bh_alloc(global_heap_allocator, 2 * POINTER_SIZE);
-    tmp_data[0] = 0;
-    tmp_data[1] = block_count;
-    WasmDatum foreign_table_global_data = {
-        .alignment = POINTER_SIZE,
-        .length = 2 * POINTER_SIZE,
-        .data = tmp_data,
-    };
-    emit_data_entry(module, &foreign_table_global_data);
-
-    {
-        DatumPatchInfo patch;
-        patch.kind = Datum_Patch_Data;
-        patch.data_id = foreign_table_data.id;
-        patch.location = 0;
-        patch.index = foreign_table_global_data.id;
-        patch.offset = 0;
-        bh_arr_push(module->data_patches, patch);
-    }
-
-    return foreign_table_global_data.id;
-
-#undef WRITE_SLICE
-#undef WRITE_PTR
-#undef PATCH
-}
-
-
-
-static u64 build_tagged_procedures(OnyxWasmModule *module) {
-    bh_arr(u32) base_patch_locations=NULL;
-    bh_arr_new(global_heap_allocator, base_patch_locations, 256);
-
-#define PATCH (bh_arr_push(base_patch_locations, tag_proc_buffer.length))
-#define WRITE_PTR(val) \
-    bh_buffer_align(&tag_proc_buffer, POINTER_SIZE); \
-    PATCH; \
-    if (POINTER_SIZE == 4) bh_buffer_write_u32(&tag_proc_buffer, val); \
-    if (POINTER_SIZE == 8) bh_buffer_write_u64(&tag_proc_buffer, val); 
-#define WRITE_SLICE(ptr, count) \
-    WRITE_PTR(ptr); \
-    if (POINTER_SIZE == 4) bh_buffer_write_u32(&tag_proc_buffer, count); \
-    if (POINTER_SIZE == 8) bh_buffer_write_u64(&tag_proc_buffer, count); 
-
-    #if (POINTER_SIZE == 4)
-        #define Tagged_Procedure_Type u32
-    #else
-        #define Tagged_Procedure_Type u64
-    #endif
-    u32 proc_count = bh_arr_length(module->procedures_with_tags);
-    Tagged_Procedure_Type* tag_proc_info = bh_alloc_array(global_heap_allocator, Tagged_Procedure_Type, proc_count); // HACK
-    memset(tag_proc_info, 0, proc_count * sizeof(Tagged_Procedure_Type));
-
-    bh_buffer tag_proc_buffer;
-    bh_buffer_init(&tag_proc_buffer, global_heap_allocator, 4096);
-
-    u32 proc_info_data_id = NEXT_DATA_ID(module);
-
-    ConstExprContext constexpr_ctx;
-    constexpr_ctx.module = module;
-    constexpr_ctx.data_id = proc_info_data_id;
-
-    // 
-    // This is necessary because 0 is an invalid offset to store in this
-    // buffer, as 0 will map to NULL. This could be a single byte insertion,
-    // but 64 bytes keeps better alignment.
-    bh_buffer_write_u64(&tag_proc_buffer, 0);
-
-    u32 index = 0;
-    bh_arr_each(AstFunction *, pfunc, module->procedures_with_tags) {
-        AstFunction *func = *pfunc;
-        if (!should_emit_function(func)) {
-            proc_count--;
-            continue;
-        }
-
-        u32 tag_count = bh_arr_length(func->tags);
-        u32 *tag_data_offsets = bh_alloc_array(global_scratch_allocator, u32, tag_count);
-        u32 *tag_data_types   = bh_alloc_array(global_scratch_allocator, u32, tag_count);
-
-        u32 tag_index = 0;
-        bh_arr_each(AstTyped *, ptag, func->tags) {
-            AstTyped *tag = *ptag;
-            bh_buffer_align(&tag_proc_buffer, type_alignment_of(tag->type));
-
-            tag_data_offsets[tag_index  ] = tag_proc_buffer.length;
-            tag_data_types  [tag_index++] = tag->type->id;
-
-            u32 size = type_size_of(tag->type);
-            bh_buffer_grow(&tag_proc_buffer, tag_proc_buffer.length + size);
-
-            constexpr_ctx.data = tag_proc_buffer.data;
-            emit_constexpr(&constexpr_ctx, tag, tag_proc_buffer.length);
-            tag_proc_buffer.length += size;
-        }
-
-        bh_buffer_align(&tag_proc_buffer, 4);
-        u32 tag_array_base = tag_proc_buffer.length;
-        fori (i, 0, tag_count) {
-            PATCH;
-            bh_buffer_write_u32(&tag_proc_buffer, tag_data_offsets[i]);
-            bh_buffer_write_u32(&tag_proc_buffer, tag_data_types[i]);
-        }
-
-        bh_buffer_align(&tag_proc_buffer, 4);
-        tag_proc_info[index++] = tag_proc_buffer.length;
-
-        bh_buffer_write_u32(&tag_proc_buffer, get_element_idx(module, func));
-        bh_buffer_write_u32(&tag_proc_buffer, func->type->id);
-        WRITE_SLICE(tag_array_base, tag_count);
-    }    
-
-    if (context.options->verbose_output == 1) {
-        bh_printf("Tagged procedure size: %d bytes.\n", tag_proc_buffer.length);
-    }
-
-    WasmDatum proc_info_data = {
-        .alignment = 8,
-        .length = tag_proc_buffer.length,
-        .data = tag_proc_buffer.data,
-    };
-    emit_data_entry(module, &proc_info_data);
-    assert(proc_info_data.id == proc_info_data_id);
-
-    bh_arr_each(u32, patch_loc, base_patch_locations) {
-        DatumPatchInfo patch;
-        patch.kind = Datum_Patch_Relative;
-        patch.data_id = proc_info_data.id;
-        patch.location = *patch_loc;
-        patch.index = proc_info_data.id;
-        patch.offset = 0;
-        bh_arr_push(module->data_patches, patch);
-    }
-
-    WasmDatum proc_table_data = {
-        .alignment = POINTER_SIZE,
-        .length = proc_count * POINTER_SIZE,
-        .data = tag_proc_info,
-    };
-    emit_data_entry(module, &proc_table_data);
-
-    fori (i, 0, proc_count) {
-        DatumPatchInfo patch;
-        patch.kind = Datum_Patch_Data;
-        patch.data_id = proc_info_data.id;
-        patch.offset = tag_proc_info[i];
-        patch.index = proc_table_data.id;
-        patch.location = i * POINTER_SIZE;
-        bh_arr_push(module->data_patches, patch);
-    }
-
-    Tagged_Procedure_Type* tmp_data = bh_alloc(global_heap_allocator, 2 * POINTER_SIZE);
-    tmp_data[0] = 0;
-    tmp_data[1] = proc_count;
-    WasmDatum proc_table_global_data = {
-        .alignment = POINTER_SIZE,
-        .length = 2 * POINTER_SIZE,
-        .data = tmp_data,
-    };
-    emit_data_entry(module, &proc_table_global_data);
-
-    {
-        DatumPatchInfo patch;
-        patch.kind = Datum_Patch_Data;
-        patch.offset = 0;
-        patch.data_id = proc_table_data.id;
-        patch.index = proc_table_global_data.id;
-        patch.location = 0;
-        bh_arr_push(module->data_patches, patch);
-    }
-
-    return proc_table_global_data.id;
-
-#undef WRITE_SLICE
-#undef WRITE_PTR
-#undef PATCH
-}
-