renamed the compiler source files.
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Wed, 8 Sep 2021 13:16:46 +0000 (08:16 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Wed, 8 Sep 2021 13:16:46 +0000 (08:16 -0500)
54 files changed:
bin/onyx
build.bat
build.sh
docs/bugs
include/astnodes.h [new file with mode: 0644]
include/doc.h [new file with mode: 0644]
include/errors.h [new file with mode: 0644]
include/lex.h [new file with mode: 0644]
include/onyxastnodes.h [deleted file]
include/onyxdoc.h [deleted file]
include/onyxerrors.h [deleted file]
include/onyxlex.h [deleted file]
include/onyxparser.h [deleted file]
include/onyxtypes.h [deleted file]
include/onyxutils.h [deleted file]
include/onyxwasm.h [deleted file]
include/parser.h [new file with mode: 0644]
include/types.h [new file with mode: 0644]
include/utils.h [new file with mode: 0644]
include/wasm.h [new file with mode: 0644]
src/astnodes.c [new file with mode: 0644]
src/builtins.c [new file with mode: 0644]
src/checker.c [new file with mode: 0644]
src/clone.c [new file with mode: 0644]
src/doc.c [new file with mode: 0644]
src/entities.c [new file with mode: 0644]
src/errors.c [new file with mode: 0644]
src/lex.c [new file with mode: 0644]
src/onyx.c
src/onyxastnodes.c [deleted file]
src/onyxbuiltins.c [deleted file]
src/onyxchecker.c [deleted file]
src/onyxclone.c [deleted file]
src/onyxdoc.c [deleted file]
src/onyxentities.c [deleted file]
src/onyxerrors.c [deleted file]
src/onyxlex.c [deleted file]
src/onyxparser.c [deleted file]
src/onyxsymres.c [deleted file]
src/onyxtypes.c [deleted file]
src/onyxutils.c [deleted file]
src/onyxwasm.c [deleted file]
src/onyxwasm_intrinsics.c [deleted file]
src/onyxwasm_output.c [deleted file]
src/onyxwasm_type_table.c [deleted file]
src/parser.c [new file with mode: 0644]
src/polymorph.c [new file with mode: 0644]
src/symres.c [new file with mode: 0644]
src/types.c [new file with mode: 0644]
src/utils.c [new file with mode: 0644]
src/wasm.c [new file with mode: 0644]
src/wasm_intrinsics.c [new file with mode: 0644]
src/wasm_output.c [new file with mode: 0644]
src/wasm_type_table.c [new file with mode: 0644]

index 8a4f175d476239d8bd10574cb4c3c6bb654a2f53..4a8dd7340b3b9d9847955ea73d45fc462557900a 100755 (executable)
Binary files a/bin/onyx and b/bin/onyx differ
index 730d3119006a2ddc7bd76f295da989daccd4b7a0..4c826e119e05d62977918f2702783cfea621455e 100644 (file)
--- a/build.bat
+++ b/build.bat
@@ -1,6 +1,6 @@
 @echo off
 
-set SOURCE_FILES=src/onyx.c src/onyxastnodes.c src/onyxbuiltins.c src/onyxchecker.c src/onyxclone.c src/onyxdoc.c src/onyxentities.c src/onyxerrors.c src/onyxlex.c src/onyxparser.c src/onyxsymres.c src/onyxtypes.c src/onyxutils.c src/onyxwasm.c
+set SOURCE_FILES=src/onyx.c src/astnodes.c src/builtins.c src/checker.c src/clone.c src/doc.c src/entities.c src/errors.c src/lex.c src/parser.c src/symres.c src/types.c src/utils.c src/wasm.c
 
 if "%1" == "1" (
     set FLAGS=/O2 /MT /Z7
@@ -13,4 +13,4 @@ del *.ilk > NUL 2> NUL
 
 cl.exe %FLAGS% /I include /std:c17 /Tc %SOURCE_FILES% /link /DEBUG /OUT:onyx.exe /incremental:no /opt:ref /subsystem:console
 
-del *.obj > NUL 2> NUL
\ No newline at end of file
+del *.obj > NUL 2> NUL
index 1558a98c867c47081e7cc46be2415f1e3a40c2e1..41bde8ac8e8f72ce3ab82225659f95dee552daf4 100755 (executable)
--- a/build.sh
+++ b/build.sh
@@ -7,7 +7,7 @@ sudo cp -r ./core/ "$CORE_DIR"
 
 [ "$1" = "libs_only" ] && exit 0
 
-C_FILES="onyx onyxastnodes onyxbuiltins onyxchecker onyxclone onyxdoc onyxentities onyxerrors onyxlex onyxparser onyxsymres onyxtypes onyxutils onyxwasm"
+C_FILES="onyx astnodes builtins checker clone doc entities errors lex parser symres types utils wasm"
 CC='gcc'
 
 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'
index 209cc77a8759824057c93c8d5304c189f82e7fbe..0cca7527f31dbfb4f8d318ec730ce45caf661d7b 100644 (file)
--- a/docs/bugs
+++ b/docs/bugs
@@ -8,6 +8,10 @@ List of known bugs:
         println(x); // Doesn't work
     }
 
+[ ] Memory reservation type checking doesn't work how I think it should. This errors for example:
+    
+    x: i64 = 1;
+
 [X] Initialization statements on control statements should be better. For example, this should
     be possible:
 
diff --git a/include/astnodes.h b/include/astnodes.h
new file mode 100644 (file)
index 0000000..e15aef5
--- /dev/null
@@ -0,0 +1,1448 @@
+#ifndef ONYXASTNODES_H
+#define ONYXASTNODES_H
+
+#include "lex.h"
+#include "types.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(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(Binding)              \
+    NODE(Alias)                \
+    NODE(MemRes)               \
+    NODE(Include)              \
+    NODE(UsePackage)           \
+    NODE(Global)               \
+    NODE(Param)                \
+    NODE(Function)             \
+    NODE(OverloadedFunction)   \
+                               \
+    NODE(PolyParam)            \
+    NODE(PolySolution)         \
+    NODE(SolidifiedFunction)   \
+    NODE(PolyProc)             \
+                               \
+    NODE(Note)                 \
+    NODE(CallSite)             \
+                               \
+    NODE(CodeBlock)            \
+    NODE(DirectiveInsert)      \
+    NODE(Macro)                \
+                               \
+    NODE(Package)          
+
+#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;
+    bh_table(AstNode *) symbols;
+} Scope;
+
+
+typedef enum AstKind {
+    Ast_Kind_Error,
+    Ast_Kind_Package,
+    Ast_Kind_Load_File,
+    Ast_Kind_Load_Path,
+    Ast_Kind_Memres,
+
+    Ast_Kind_Binding,
+    Ast_Kind_Alias,
+    Ast_Kind_Function,
+    Ast_Kind_Overloaded_Function,
+    Ast_Kind_Polymorphic_Proc,
+    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_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_Call_Site,
+
+    Ast_Kind_Code_Block,
+    Ast_Kind_Directive_Insert,
+    Ast_Kind_Macro,
+    Ast_Kind_Do_Block,
+
+    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_Exported              = BH_BIT(1),
+    Ast_Flag_Foreign               = BH_BIT(2),
+    Ast_Flag_Const                 = BH_BIT(3),
+    Ast_Flag_Comptime              = BH_BIT(4),
+    Ast_Flag_Private_Package       = BH_BIT(5),
+    Ast_Flag_Private_File          = BH_BIT(6),
+
+    // Global flags
+    Ast_Flag_Global_Stack_Top      = BH_BIT(7),
+
+    // Function flags
+    Ast_Flag_Already_Checked       = BH_BIT(8),
+    Ast_Flag_Intrinsic             = BH_BIT(10),
+    Ast_Flag_Function_Used         = BH_BIT(11),
+
+    // Expression flags
+    Ast_Flag_Expr_Ignored          = BH_BIT(13),
+    Ast_Flag_Param_Use             = BH_BIT(14), // Unneeded, just use a bool on AstParam
+    Ast_Flag_Address_Taken         = BH_BIT(15),
+
+    // Type flags
+    Ast_Flag_Type_Is_Resolved      = BH_BIT(16),
+
+    // Enum flags
+    Ast_Flag_Enum_Is_Flags         = BH_BIT(17), // Unneeded, just use a bool on AstEnum
+
+    // Struct flags
+    Ast_Flag_Struct_Is_Union       = BH_BIT(18), // Unneeded, just a usea bool on AstStruct
+
+    Ast_Flag_No_Clone              = BH_BIT(19),
+
+    Ast_Flag_Cannot_Take_Addr      = BH_BIT(20),
+
+    Ast_Flag_Struct_Mem_Used       = BH_BIT(21), // Unneeded, just a usea bool on AstStructMember
+
+    // HACK: NullProcHack
+    Ast_Flag_Proc_Is_Null          = BH_BIT(22),
+
+    Ast_Flag_From_Polymorphism     = BH_BIT(23),
+
+    Ast_Flag_Incomplete_Body       = BH_BIT(24),
+
+    Ast_Flag_Array_Literal_Typed   = BH_BIT(25),
+
+    Ast_Flag_Has_Been_Symres       = BH_BIT(26),
+
+    Ast_Flag_Has_Been_Checked      = BH_BIT(27),
+
+    Ast_Flag_Static_If_Resolved    = BH_BIT(28),
+
+    // :TEMPORARY
+    Ast_Flag_Params_Introduced     = BH_BIT(29),
+} 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_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_ZERO_VALUE,
+
+    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,
+} 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 struct Arguments Arguments;
+struct Arguments {
+    bh_arr(AstTyped *) values;
+    bh_arr(AstNamedValue *) named_values;
+};
+
+
+// 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 AstBinaryOp      { AstTyped_base; BinaryOp operation; AstTyped *left, *right; };
+struct AstUnaryOp       { AstTyped_base; UnaryOp operation; AstTyped *expr; };
+struct AstNumLit        { AstTyped_base; union { i32 i; i64 l; f32 f; f64 d; } value; };
+struct AstStrLit        { AstTyped_base; u64 addr; u64 length; };
+struct AstLocal         { AstTyped_base; };
+struct AstArgument      { AstTyped_base; AstTyped *value; VarArgKind va_kind; b32 is_baked : 1; };
+struct AstAddressOf     { AstTyped_base; AstTyped *expr; };
+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 AstSubscript   {
+    AstTyped_base;
+    BinaryOp __unused_operation; // This will be set to Binary_Op_Subscript
+    AstTyped *addr;
+    AstTyped *expr;
+    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;
+
+    OnyxToken *filename_token;
+    char *filename; // The parsed file name, with '\' sequences removed and resolved to a particular file if possible.
+
+    u32 addr, 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 original_args;
+    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 AstDirectiveSolidify {
+    AstTyped_base;
+
+    AstPolyProc* 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;
+};
+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;
+};
+struct AstIfWhile {
+    AstNode_base;
+
+    Scope *scope;
+    AstNode* initialization;
+
+    AstTyped *cond;
+
+    AstBlock *true_stmt;
+    AstBlock *false_stmt;
+
+    // Used by Static_If
+    bh_arr(struct Entity *) true_entities;
+    bh_arr(struct Entity *) false_entities;
+};
+typedef struct AstIfWhile AstIf;
+typedef struct AstIfWhile AstWhile;
+
+struct AstSwitchCase {
+    // NOTE: All expressions that end up in this block
+    bh_arr(AstTyped *) values;
+    
+    AstBlock *block;
+};
+struct AstSwitch {
+    AstNode_base;
+
+    Scope *scope;
+    AstNode* initialization;
+
+    AstTyped *expr;
+
+    bh_arr(AstSwitchCase) cases;
+    AstBlock *default_case;
+
+    // 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;
+};
+
+// 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; \
+    u64 type_id;           \
+    void* __unused;        \
+    Type* type
+struct AstType { AstType_base; };
+
+struct AstBasicType     { AstType_base; Type* basic_type; };
+struct AstPointerType   { AstType_base; AstType* elem; };
+struct AstFunctionType  { AstType_base; AstType* return_type; u64 param_count; AstType* params[]; };
+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 AstStructType {
+    AstType_base;
+    char *name;
+
+    bh_arr(AstStructMember *) members;
+
+    u32 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: Used to store statically bound expressions in the struct.
+    Scope* scope;
+
+    struct Entity* entity_type;
+    struct Entity* entity_defaults;
+
+    b32 stcache_is_valid : 1;
+};
+struct AstStructMember {
+    AstTyped_base;
+    AstTyped* initial_value;
+};
+struct AstPolyStructParam {
+    AstTyped_base;
+};
+struct AstPolyStructType {
+    AstType_base;
+    char *name;
+
+    Scope *scope;
+    bh_arr(AstPolyStructParam) poly_params;
+    bh_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;
+};
+struct AstEnumValue    { AstTyped_base; AstNumLit* 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;
+};
+
+// Top level nodes
+struct AstBinding       { AstTyped_base; AstNode* node; };
+struct AstAlias         { AstTyped_base; AstTyped* alias; };
+struct AstMemRes        { AstTyped_base; u64 addr; AstTyped *initial_value; };
+struct AstInclude       { AstNode_base; char* name; };
+struct AstGlobal        {
+    AstTyped_base;
+
+    OnyxToken* 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 use_processed : 1;
+};
+struct AstFunction {
+    AstTyped_base;
+
+    Scope *scope;
+
+    bh_arr(AstParam) params;
+    AstType* return_type;
+
+    AstBlock *body;
+
+    OnyxToken* 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;
+
+    // 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;
+
+    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;
+};
+
+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;
+};
+
+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;
+
+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;
+    Scope*       poly_scope;
+
+    b32 header_complete: 1;
+};
+
+struct AstPolyProc {
+    // While the "type" of a polymorphic procedure will never be set, it is necessary
+    // for contexts where it used in an expression.
+    AstTyped_base;
+
+    Scope *poly_scope;
+    bh_arr(AstPolyParam) poly_params;
+
+    bh_arr(AstPolySolution) known_slns;
+
+    AstFunction* base_func;
+    bh_table(AstSolidifiedFunction) concrete_funcs;
+};
+
+
+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;
+};
+
+struct AstDirectiveDefined {
+    AstTyped_base;
+    AstTyped *expr;
+
+    b32 is_defined: 1;
+};
+
+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 {
+    AstNode_base;
+
+    AstTyped *code_expr;
+};
+
+struct AstMacro {
+    AstTyped_base;
+
+    AstTyped* body;
+};
+
+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_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_Type_Alias,
+    Entity_Type_Memory_Reservation_Type,
+    Entity_Type_Use,
+    Entity_Type_Polymorphic_Proc,
+    Entity_Type_Macro,
+    Entity_Type_Foreign_Function_Header,
+    Entity_Type_Foreign_Global_Header,
+    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;
+        AstMemRes             *mem_res;
+        AstPolyProc           *poly_proc;
+        AstMacro              *macro;
+        AstUse                *use;
+    };
+} 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);
+
+// 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);
+
+b32  entity_bring_to_state(Entity* ent, EntityState state);
+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_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_Wasi    = 1,
+    Runtime_Js      = 2,
+    Runtime_Custom  = 3,
+};
+
+
+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;
+
+    Runtime runtime;
+
+    bh_arr(const char *) included_folders;
+    bh_arr(const char *) files;
+    const char* target_file;
+    const char* documentation_file;
+};
+
+typedef struct Context Context;
+struct Context {
+    bh_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_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 AstNumLit builtin_heap_start;
+extern AstGlobal builtin_stack_top;
+extern AstType  *builtin_string_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 AstTyped *type_table_node;
+
+typedef struct BuiltinSymbol {
+    char*    package;
+    char*    sym;
+    AstNode* node;
+} BuiltinSymbol;
+
+extern const BuiltinSymbol builtin_symbols[];
+
+typedef struct IntrinsicMap {
+    char*         name;
+    OnyxIntrinsic intrinsic;
+} IntrinsicMap;
+
+extern bh_table(OnyxIntrinsic) intrinsic_table;
+
+extern bh_arr(OverloadOption) operator_overloads[Binary_Op_Count];
+
+void initialize_builtins(bh_allocator a);
+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);
+
+b32 type_check_or_auto_cast(AstTyped** pnode, Type* type);
+Type* resolve_expression_type(AstTyped* node);
+i64 get_expression_integer_value(AstTyped* node);
+
+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);
+
+void arguments_initialize(Arguments* args);
+b32 fill_in_arguments(Arguments* args, AstNode* provider, char** err_msg);
+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);
+
+// GROSS: Using void* to avoid having to cast everything.
+const char* node_get_type_name(void* node);
+
+b32 static_if_resolution(AstIf* static_if);
+
+typedef enum PolyProcLookupMethod {
+    PPLM_By_Arguments,
+    PPLM_By_Function_Type,
+} PolyProcLookupMethod;
+AstFunction* polymorphic_proc_lookup(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual, OnyxToken* tkn);
+AstFunction* polymorphic_proc_solidify(AstPolyProc* pp, bh_arr(AstPolySolution) slns, OnyxToken* tkn);
+AstNode* polymorphic_proc_try_solidify(AstPolyProc* pp, bh_arr(AstPolySolution) slns, OnyxToken* tkn);
+AstFunction* polymorphic_proc_build_only_header(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual);
+
+void add_overload_option(bh_arr(OverloadOption)* poverloads, u64 precedence, AstTyped* overload);
+AstTyped* find_matching_overload_by_arguments(bh_arr(OverloadOption) overloads, Arguments* args, b32* should_yield);
+AstTyped* find_matching_overload_by_type(bh_arr(OverloadOption) overloads, Type* type);
+void report_unable_to_match_overload(AstCall* call);
+
+void expand_macro(AstCall** pcall, AstFunction* template);
+AstFunction* macro_resolve_header(AstMacro* macro, Arguments* args, OnyxToken* callsite);
+
+AstStructType* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(AstPolySolution) slns, OnyxFilePos pos);
+
+// NOTE: Useful inlined functions
+static inline b32 is_lval(AstNode* 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))
+        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 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 ((AstPolyProc *) node)->base_func;
+    if (node->kind == Ast_Kind_Macro) return get_function_from_node((AstNode*) ((AstMacro *) node)->body);
+    return NULL;
+}
+
+#endif // #ifndef ONYXASTNODES_H
diff --git a/include/doc.h b/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/include/errors.h b/include/errors.h
new file mode 100644 (file)
index 0000000..41aebdd
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef ONYXERRORS_H
+#define ONYXERRORS_H
+
+#include "bh.h"
+#include "lex.h"
+
+#include <stdarg.h>
+
+typedef struct OnyxError {
+    OnyxFilePos pos;
+    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_report_error(OnyxFilePos pos, char * format, ...);
+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
new file mode 100644 (file)
index 0000000..43fed67
--- /dev/null
@@ -0,0 +1,121 @@
+#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_Proc,
+    Token_Type_Keyword_As,
+    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_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);
+
+#endif
diff --git a/include/onyxastnodes.h b/include/onyxastnodes.h
deleted file mode 100644 (file)
index a4f5f55..0000000
+++ /dev/null
@@ -1,1448 +0,0 @@
-#ifndef ONYXASTNODES_H
-#define ONYXASTNODES_H
-
-#include "onyxlex.h"
-#include "onyxtypes.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(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(Binding)              \
-    NODE(Alias)                \
-    NODE(MemRes)               \
-    NODE(Include)              \
-    NODE(UsePackage)           \
-    NODE(Global)               \
-    NODE(Param)                \
-    NODE(Function)             \
-    NODE(OverloadedFunction)   \
-                               \
-    NODE(PolyParam)            \
-    NODE(PolySolution)         \
-    NODE(SolidifiedFunction)   \
-    NODE(PolyProc)             \
-                               \
-    NODE(Note)                 \
-    NODE(CallSite)             \
-                               \
-    NODE(CodeBlock)            \
-    NODE(DirectiveInsert)      \
-    NODE(Macro)                \
-                               \
-    NODE(Package)          
-
-#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;
-    bh_table(AstNode *) symbols;
-} Scope;
-
-
-typedef enum AstKind {
-    Ast_Kind_Error,
-    Ast_Kind_Package,
-    Ast_Kind_Load_File,
-    Ast_Kind_Load_Path,
-    Ast_Kind_Memres,
-
-    Ast_Kind_Binding,
-    Ast_Kind_Alias,
-    Ast_Kind_Function,
-    Ast_Kind_Overloaded_Function,
-    Ast_Kind_Polymorphic_Proc,
-    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_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_Call_Site,
-
-    Ast_Kind_Code_Block,
-    Ast_Kind_Directive_Insert,
-    Ast_Kind_Macro,
-    Ast_Kind_Do_Block,
-
-    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_Exported              = BH_BIT(1),
-    Ast_Flag_Foreign               = BH_BIT(2),
-    Ast_Flag_Const                 = BH_BIT(3),
-    Ast_Flag_Comptime              = BH_BIT(4),
-    Ast_Flag_Private_Package       = BH_BIT(5),
-    Ast_Flag_Private_File          = BH_BIT(6),
-
-    // Global flags
-    Ast_Flag_Global_Stack_Top      = BH_BIT(7),
-
-    // Function flags
-    Ast_Flag_Already_Checked       = BH_BIT(8),
-    Ast_Flag_Intrinsic             = BH_BIT(10),
-    Ast_Flag_Function_Used         = BH_BIT(11),
-
-    // Expression flags
-    Ast_Flag_Expr_Ignored          = BH_BIT(13),
-    Ast_Flag_Param_Use             = BH_BIT(14), // Unneeded, just use a bool on AstParam
-    Ast_Flag_Address_Taken         = BH_BIT(15),
-
-    // Type flags
-    Ast_Flag_Type_Is_Resolved      = BH_BIT(16),
-
-    // Enum flags
-    Ast_Flag_Enum_Is_Flags         = BH_BIT(17), // Unneeded, just use a bool on AstEnum
-
-    // Struct flags
-    Ast_Flag_Struct_Is_Union       = BH_BIT(18), // Unneeded, just a usea bool on AstStruct
-
-    Ast_Flag_No_Clone              = BH_BIT(19),
-
-    Ast_Flag_Cannot_Take_Addr      = BH_BIT(20),
-
-    Ast_Flag_Struct_Mem_Used       = BH_BIT(21), // Unneeded, just a usea bool on AstStructMember
-
-    // HACK: NullProcHack
-    Ast_Flag_Proc_Is_Null          = BH_BIT(22),
-
-    Ast_Flag_From_Polymorphism     = BH_BIT(23),
-
-    Ast_Flag_Incomplete_Body       = BH_BIT(24),
-
-    Ast_Flag_Array_Literal_Typed   = BH_BIT(25),
-
-    Ast_Flag_Has_Been_Symres       = BH_BIT(26),
-
-    Ast_Flag_Has_Been_Checked      = BH_BIT(27),
-
-    Ast_Flag_Static_If_Resolved    = BH_BIT(28),
-
-    // :TEMPORARY
-    Ast_Flag_Params_Introduced     = BH_BIT(29),
-} 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_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_ZERO_VALUE,
-
-    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,
-} 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 struct Arguments Arguments;
-struct Arguments {
-    bh_arr(AstTyped *) values;
-    bh_arr(AstNamedValue *) named_values;
-};
-
-
-// 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 AstBinaryOp      { AstTyped_base; BinaryOp operation; AstTyped *left, *right; };
-struct AstUnaryOp       { AstTyped_base; UnaryOp operation; AstTyped *expr; };
-struct AstNumLit        { AstTyped_base; union { i32 i; i64 l; f32 f; f64 d; } value; };
-struct AstStrLit        { AstTyped_base; u64 addr; u64 length; };
-struct AstLocal         { AstTyped_base; };
-struct AstArgument      { AstTyped_base; AstTyped *value; VarArgKind va_kind; b32 is_baked : 1; };
-struct AstAddressOf     { AstTyped_base; AstTyped *expr; };
-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 AstSubscript   {
-    AstTyped_base;
-    BinaryOp __unused_operation; // This will be set to Binary_Op_Subscript
-    AstTyped *addr;
-    AstTyped *expr;
-    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;
-
-    OnyxToken *filename_token;
-    char *filename; // The parsed file name, with '\' sequences removed and resolved to a particular file if possible.
-
-    u32 addr, 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 original_args;
-    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 AstDirectiveSolidify {
-    AstTyped_base;
-
-    AstPolyProc* 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;
-};
-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;
-};
-struct AstIfWhile {
-    AstNode_base;
-
-    Scope *scope;
-    AstNode* initialization;
-
-    AstTyped *cond;
-
-    AstBlock *true_stmt;
-    AstBlock *false_stmt;
-
-    // Used by Static_If
-    bh_arr(struct Entity *) true_entities;
-    bh_arr(struct Entity *) false_entities;
-};
-typedef struct AstIfWhile AstIf;
-typedef struct AstIfWhile AstWhile;
-
-struct AstSwitchCase {
-    // NOTE: All expressions that end up in this block
-    bh_arr(AstTyped *) values;
-    
-    AstBlock *block;
-};
-struct AstSwitch {
-    AstNode_base;
-
-    Scope *scope;
-    AstNode* initialization;
-
-    AstTyped *expr;
-
-    bh_arr(AstSwitchCase) cases;
-    AstBlock *default_case;
-
-    // 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;
-};
-
-// 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; \
-    u64 type_id;           \
-    void* __unused;        \
-    Type* type
-struct AstType { AstType_base; };
-
-struct AstBasicType     { AstType_base; Type* basic_type; };
-struct AstPointerType   { AstType_base; AstType* elem; };
-struct AstFunctionType  { AstType_base; AstType* return_type; u64 param_count; AstType* params[]; };
-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 AstStructType {
-    AstType_base;
-    char *name;
-
-    bh_arr(AstStructMember *) members;
-
-    u32 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: Used to store statically bound expressions in the struct.
-    Scope* scope;
-
-    struct Entity* entity_type;
-    struct Entity* entity_defaults;
-
-    b32 stcache_is_valid : 1;
-};
-struct AstStructMember {
-    AstTyped_base;
-    AstTyped* initial_value;
-};
-struct AstPolyStructParam {
-    AstTyped_base;
-};
-struct AstPolyStructType {
-    AstType_base;
-    char *name;
-
-    Scope *scope;
-    bh_arr(AstPolyStructParam) poly_params;
-    bh_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;
-};
-struct AstEnumValue    { AstTyped_base; AstNumLit* 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;
-};
-
-// Top level nodes
-struct AstBinding       { AstTyped_base; AstNode* node; };
-struct AstAlias         { AstTyped_base; AstTyped* alias; };
-struct AstMemRes        { AstTyped_base; u64 addr; AstTyped *initial_value; };
-struct AstInclude       { AstNode_base; char* name; };
-struct AstGlobal        {
-    AstTyped_base;
-
-    OnyxToken* 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 use_processed : 1;
-};
-struct AstFunction {
-    AstTyped_base;
-
-    Scope *scope;
-
-    bh_arr(AstParam) params;
-    AstType* return_type;
-
-    AstBlock *body;
-
-    OnyxToken* 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;
-
-    // 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;
-
-    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;
-};
-
-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;
-};
-
-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;
-
-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;
-    Scope*       poly_scope;
-
-    b32 header_complete: 1;
-};
-
-struct AstPolyProc {
-    // While the "type" of a polymorphic procedure will never be set, it is necessary
-    // for contexts where it used in an expression.
-    AstTyped_base;
-
-    Scope *poly_scope;
-    bh_arr(AstPolyParam) poly_params;
-
-    bh_arr(AstPolySolution) known_slns;
-
-    AstFunction* base_func;
-    bh_table(AstSolidifiedFunction) concrete_funcs;
-};
-
-
-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;
-};
-
-struct AstDirectiveDefined {
-    AstTyped_base;
-    AstTyped *expr;
-
-    b32 is_defined: 1;
-};
-
-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 {
-    AstNode_base;
-
-    AstTyped *code_expr;
-};
-
-struct AstMacro {
-    AstTyped_base;
-
-    AstTyped* body;
-};
-
-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_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_Type_Alias,
-    Entity_Type_Memory_Reservation_Type,
-    Entity_Type_Use,
-    Entity_Type_Polymorphic_Proc,
-    Entity_Type_Macro,
-    Entity_Type_Foreign_Function_Header,
-    Entity_Type_Foreign_Global_Header,
-    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;
-        AstMemRes             *mem_res;
-        AstPolyProc           *poly_proc;
-        AstMacro              *macro;
-        AstUse                *use;
-    };
-} 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);
-
-// 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);
-
-b32  entity_bring_to_state(Entity* ent, EntityState state);
-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_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_Wasi    = 1,
-    Runtime_Js      = 2,
-    Runtime_Custom  = 3,
-};
-
-
-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;
-
-    Runtime runtime;
-
-    bh_arr(const char *) included_folders;
-    bh_arr(const char *) files;
-    const char* target_file;
-    const char* documentation_file;
-};
-
-typedef struct Context Context;
-struct Context {
-    bh_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_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 AstNumLit builtin_heap_start;
-extern AstGlobal builtin_stack_top;
-extern AstType  *builtin_string_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 AstTyped *type_table_node;
-
-typedef struct BuiltinSymbol {
-    char*    package;
-    char*    sym;
-    AstNode* node;
-} BuiltinSymbol;
-
-extern const BuiltinSymbol builtin_symbols[];
-
-typedef struct IntrinsicMap {
-    char*         name;
-    OnyxIntrinsic intrinsic;
-} IntrinsicMap;
-
-extern bh_table(OnyxIntrinsic) intrinsic_table;
-
-extern bh_arr(OverloadOption) operator_overloads[Binary_Op_Count];
-
-void initialize_builtins(bh_allocator a);
-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);
-
-b32 type_check_or_auto_cast(AstTyped** pnode, Type* type);
-Type* resolve_expression_type(AstTyped* node);
-i64 get_expression_integer_value(AstTyped* node);
-
-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);
-
-void arguments_initialize(Arguments* args);
-b32 fill_in_arguments(Arguments* args, AstNode* provider, char** err_msg);
-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);
-
-// GROSS: Using void* to avoid having to cast everything.
-const char* node_get_type_name(void* node);
-
-b32 static_if_resolution(AstIf* static_if);
-
-typedef enum PolyProcLookupMethod {
-    PPLM_By_Arguments,
-    PPLM_By_Function_Type,
-} PolyProcLookupMethod;
-AstFunction* polymorphic_proc_lookup(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual, OnyxToken* tkn);
-AstFunction* polymorphic_proc_solidify(AstPolyProc* pp, bh_arr(AstPolySolution) slns, OnyxToken* tkn);
-AstNode* polymorphic_proc_try_solidify(AstPolyProc* pp, bh_arr(AstPolySolution) slns, OnyxToken* tkn);
-AstFunction* polymorphic_proc_build_only_header(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual);
-
-void add_overload_option(bh_arr(OverloadOption)* poverloads, u64 precedence, AstTyped* overload);
-AstTyped* find_matching_overload_by_arguments(bh_arr(OverloadOption) overloads, Arguments* args, b32* should_yield);
-AstTyped* find_matching_overload_by_type(bh_arr(OverloadOption) overloads, Type* type);
-void report_unable_to_match_overload(AstCall* call);
-
-void expand_macro(AstCall** pcall, AstFunction* template);
-AstFunction* macro_resolve_header(AstMacro* macro, Arguments* args, OnyxToken* callsite);
-
-AstStructType* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(AstPolySolution) slns, OnyxFilePos pos);
-
-// NOTE: Useful inlined functions
-static inline b32 is_lval(AstNode* 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))
-        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 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 ((AstPolyProc *) node)->base_func;
-    if (node->kind == Ast_Kind_Macro) return get_function_from_node((AstNode*) ((AstMacro *) node)->body);
-    return NULL;
-}
-
-#endif // #ifndef ONYXASTNODES_H
diff --git a/include/onyxdoc.h b/include/onyxdoc.h
deleted file mode 100644 (file)
index 0e6e505..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-#ifndef ONYXDOC_H
-#define ONYXDOC_H
-
-#include "bh.h"
-#include "onyxastnodes.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/onyxerrors.h b/include/onyxerrors.h
deleted file mode 100644 (file)
index 993f4f7..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-#ifndef ONYXERRORS_H
-#define ONYXERRORS_H
-
-#include "bh.h"
-#include "onyxlex.h"
-
-#include <stdarg.h>
-
-typedef struct OnyxError {
-    OnyxFilePos pos;
-    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_report_error(OnyxFilePos pos, char * format, ...);
-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/onyxlex.h b/include/onyxlex.h
deleted file mode 100644 (file)
index 43fed67..0000000
+++ /dev/null
@@ -1,121 +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_Proc,
-    Token_Type_Keyword_As,
-    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_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);
-
-#endif
diff --git a/include/onyxparser.h b/include/onyxparser.h
deleted file mode 100644 (file)
index 712ac28..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-#ifndef ONYXPARSER_H
-#define ONYXPARSER_H
-
-#include "bh.h"
-
-#include "onyxlex.h"
-#include "onyxerrors.h"
-#include "onyxastnodes.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;
-
-    Scope *current_scope;
-    bh_arr(bh_arr(Entity *) *) alternate_entity_placement_stack;
-
-    bh_arr(AstFlags) scope_flags;
-
-    b32 hit_unexpected_token : 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/onyxtypes.h b/include/onyxtypes.h
deleted file mode 100644 (file)
index 03574c7..0000000
+++ /dev/null
@@ -1,221 +0,0 @@
-#ifndef ONYXTYPES_H
-#define ONYXTYPES_H
-
-#include "bh.h"
-
-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 AstTyped** initial_value;
-    b32 included_through_use : 1;
-    b32 used : 1;
-} StructMember;
-
-typedef struct TypeWithOffset TypeWithOffset;
-struct TypeWithOffset {
-    Type* type;
-    u32   offset;
-};
-
-#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;                                 \
-        bh_table(StructMember) members;                           \
-        bh_arr(StructMember *) memarr;                            \
-        bh_arr(struct AstPolySolution) poly_sln;                  \
-        bh_arr(TypeWithOffset) linear_members;                    \
-        struct AstType *constructed_from;                         \
-    })                                                            \
-    TYPE_KIND(Compound, struct {                                  \
-        u32 count;                                                \
-        u32 size;                                                 \
-        bh_arr(TypeWithOffset) linear_members;                    \
-        Type* types[];                                            \
-    })                                                            \
-    TYPE_KIND(Array, struct { u32 size; u32 count; Type *elem; }) \
-    TYPE_KIND(Slice, struct { Type *ptr_to_data; })               \
-    TYPE_KIND(DynArray, struct { Type *ptr_to_data; })            \
-    TYPE_KIND(VarArgs, struct { Type *ptr_to_data; })             \
-    TYPE_KIND(Enum, struct {                                      \
-        char* name;                                               \
-        Type* backing;                                            \
-        b32   is_flags;                                           \
-    })
-
-
-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;
-
-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_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);
-
-const char* type_get_unique_name(Type* type);
-const char* type_get_name(Type* type);
-u32 type_get_alignment_log2(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/onyxutils.h b/include/onyxutils.h
deleted file mode 100644 (file)
index 9695788..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-#include "bh.h"
-
-#include "onyxastnodes.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);
-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);
-
-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);
-
-// NOTE: This should not be called until immediately before using the return value.
-// This function can return a static variable which will change if this is called
-// another time.                                        -brendanfh 2020/10/09
-// :RelativeFiles This should lookup for the file relative to "relative_to"
-char* lookup_included_file(char* filename, char* relative_to, b32 add_onyx_suffix, b32 search_included_folders);
-
-extern AstTyped node_that_signals_a_yield;
diff --git a/include/onyxwasm.h b/include/onyxwasm.h
deleted file mode 100644 (file)
index b423403..0000000
+++ /dev/null
@@ -1,592 +0,0 @@
-#ifndef ONYXWASM_H
-#define ONYXWASM_H
-
-#include "bh.h"
-
-#include "onyxastnodes.h"
-#include "onyxerrors.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
-
-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_COPY                   = EXT_INSTR_MASK | 0x0a,
-    WI_MEMORY_FILL                   = EXT_INSTR_MASK | 0x0b,
-} 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;
-} 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;
-    i32 idx;
-    OnyxToken *mod, *name;
-} WasmImport;
-
-typedef struct WasmDatum {
-    u32 offset, length;
-    ptr data;
-} WasmDatum;
-
-typedef enum DeferredStmtType {
-    Deferred_Stmt_Node,
-    Deferred_Stmt_Code,
-} DeferredStmtType;
-
-typedef struct DeferredStmt {
-    DeferredStmtType type;
-    u32 depth;
-
-    union {
-        AstNode *stmt;
-        struct {
-            WasmInstruction* instructions;
-            u32 instruction_count;
-        };
-    };
-} DeferredStmt;
-
-typedef struct AllocatedSpace {
-    u64 depth;
-    AstTyped *expr;
-} AllocatedSpace;
-
-typedef struct StrLitInfo {
-    u32 addr;
-    u32 len;
-} StrLitInfo;
-
-typedef struct PatchInfo {
-    u32 instruction_index;
-} PatchInfo;
-
-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;
-
-    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;
-
-    // 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.
-    bh_table(i32) type_map;
-
-    bh_table(StrLitInfo) loaded_file_info;
-    bh_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;
-    bh_table(WasmExport)  exports;
-    bh_arr(WasmGlobal)    globals;
-    bh_arr(WasmFunc)      funcs;
-    bh_arr(WasmDatum)     data;
-    bh_arr(i32)           elems;
-
-    // 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_datum_offset;
-    u32 next_elem_idx;
-    u32 foreign_function_count;
-    u32 foreign_global_count;
-
-    i32 *stack_top_ptr;
-    u64 stack_base_idx;
-    CallingConvention curr_cc;
-    i32 null_proc_func_idx;
-
-    b32 has_stack_locals : 1;
-} OnyxWasmModule;
-
-OnyxWasmModule onyx_wasm_module_create(bh_allocator alloc);
-void onyx_wasm_module_free(OnyxWasmModule* module);
-void onyx_wasm_module_write_to_file(OnyxWasmModule* module, bh_file file);
-
-#endif
diff --git a/include/parser.h b/include/parser.h
new file mode 100644 (file)
index 0000000..bfad941
--- /dev/null
@@ -0,0 +1,42 @@
+#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;
+
+    Scope *current_scope;
+    bh_arr(bh_arr(Entity *) *) alternate_entity_placement_stack;
+
+    bh_arr(AstFlags) scope_flags;
+
+    b32 hit_unexpected_token : 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/types.h b/include/types.h
new file mode 100644 (file)
index 0000000..03574c7
--- /dev/null
@@ -0,0 +1,221 @@
+#ifndef ONYXTYPES_H
+#define ONYXTYPES_H
+
+#include "bh.h"
+
+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 AstTyped** initial_value;
+    b32 included_through_use : 1;
+    b32 used : 1;
+} StructMember;
+
+typedef struct TypeWithOffset TypeWithOffset;
+struct TypeWithOffset {
+    Type* type;
+    u32   offset;
+};
+
+#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;                                 \
+        bh_table(StructMember) members;                           \
+        bh_arr(StructMember *) memarr;                            \
+        bh_arr(struct AstPolySolution) poly_sln;                  \
+        bh_arr(TypeWithOffset) linear_members;                    \
+        struct AstType *constructed_from;                         \
+    })                                                            \
+    TYPE_KIND(Compound, struct {                                  \
+        u32 count;                                                \
+        u32 size;                                                 \
+        bh_arr(TypeWithOffset) linear_members;                    \
+        Type* types[];                                            \
+    })                                                            \
+    TYPE_KIND(Array, struct { u32 size; u32 count; Type *elem; }) \
+    TYPE_KIND(Slice, struct { Type *ptr_to_data; })               \
+    TYPE_KIND(DynArray, struct { Type *ptr_to_data; })            \
+    TYPE_KIND(VarArgs, struct { Type *ptr_to_data; })             \
+    TYPE_KIND(Enum, struct {                                      \
+        char* name;                                               \
+        Type* backing;                                            \
+        b32   is_flags;                                           \
+    })
+
+
+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;
+
+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_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);
+
+const char* type_get_unique_name(Type* type);
+const char* type_get_name(Type* type);
+u32 type_get_alignment_log2(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
new file mode 100644 (file)
index 0000000..48c58f5
--- /dev/null
@@ -0,0 +1,42 @@
+#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);
+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);
+
+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);
+
+// NOTE: This should not be called until immediately before using the return value.
+// This function can return a static variable which will change if this is called
+// another time.                                        -brendanfh 2020/10/09
+// :RelativeFiles This should lookup for the file relative to "relative_to"
+char* lookup_included_file(char* filename, char* relative_to, b32 add_onyx_suffix, b32 search_included_folders);
+
+extern AstTyped node_that_signals_a_yield;
diff --git a/include/wasm.h b/include/wasm.h
new file mode 100644 (file)
index 0000000..7d0ddc3
--- /dev/null
@@ -0,0 +1,592 @@
+#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
+
+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_COPY                   = EXT_INSTR_MASK | 0x0a,
+    WI_MEMORY_FILL                   = EXT_INSTR_MASK | 0x0b,
+} 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;
+} 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;
+    i32 idx;
+    OnyxToken *mod, *name;
+} WasmImport;
+
+typedef struct WasmDatum {
+    u32 offset, length;
+    ptr data;
+} WasmDatum;
+
+typedef enum DeferredStmtType {
+    Deferred_Stmt_Node,
+    Deferred_Stmt_Code,
+} DeferredStmtType;
+
+typedef struct DeferredStmt {
+    DeferredStmtType type;
+    u32 depth;
+
+    union {
+        AstNode *stmt;
+        struct {
+            WasmInstruction* instructions;
+            u32 instruction_count;
+        };
+    };
+} DeferredStmt;
+
+typedef struct AllocatedSpace {
+    u64 depth;
+    AstTyped *expr;
+} AllocatedSpace;
+
+typedef struct StrLitInfo {
+    u32 addr;
+    u32 len;
+} StrLitInfo;
+
+typedef struct PatchInfo {
+    u32 instruction_index;
+} PatchInfo;
+
+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;
+
+    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;
+
+    // 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.
+    bh_table(i32) type_map;
+
+    bh_table(StrLitInfo) loaded_file_info;
+    bh_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;
+    bh_table(WasmExport)  exports;
+    bh_arr(WasmGlobal)    globals;
+    bh_arr(WasmFunc)      funcs;
+    bh_arr(WasmDatum)     data;
+    bh_arr(i32)           elems;
+
+    // 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_datum_offset;
+    u32 next_elem_idx;
+    u32 foreign_function_count;
+    u32 foreign_global_count;
+
+    i32 *stack_top_ptr;
+    u64 stack_base_idx;
+    CallingConvention curr_cc;
+    i32 null_proc_func_idx;
+
+    b32 has_stack_locals : 1;
+} OnyxWasmModule;
+
+OnyxWasmModule onyx_wasm_module_create(bh_allocator alloc);
+void onyx_wasm_module_free(OnyxWasmModule* module);
+void onyx_wasm_module_write_to_file(OnyxWasmModule* module, bh_file file);
+
+#endif
diff --git a/src/astnodes.c b/src/astnodes.c
new file mode 100644 (file)
index 0000000..7cdbc44
--- /dev/null
@@ -0,0 +1,1046 @@
+#include "astnodes.h"
+#include "parser.h"
+#include "utils.h"
+
+static const char* ast_node_names[] = {
+    "ERROR",
+    "PACKAGE",
+    "INCLUDE FILE",
+    "INCLUDE FOLDER",
+    "MEMORY RESERVATION",
+
+    "BINDING",
+    "ALIAS",
+    "FUNCTION",
+    "OVERLOADED_FUNCTION",
+    "POLYMORPHIC PROC",
+    "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",
+    "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",
+    "SWITCH CASE",
+
+    "SOLIDIFY",
+    "STATIC IF",
+    "STATIC ERROR",
+    "ADD OVERLOAD",
+    "OPERATOR OVERLOAD",
+    "EXPORT",
+    "DEFINED",
+    "CALL SITE",
+
+    "CODE BLOCK",
+    "DIRECTIVE INSERT",
+    "MACRO",
+    "DO BLOCK",
+
+    "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",
+};
+
+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",
+    "Type Alias",
+    "Memory Reservation Type",
+    "Use",
+    "Polymorphic Proc",
+    "Macro",
+    "Foreign_Function Header",
+    "Foreign_Global Header",
+    "Function Header",
+    "Global Header",
+    "Process Directive",
+    "Struct Member Default",
+    "Memory Reservation",
+    "Expression",
+    "Global",
+    "Overloaded_Function",
+    "Function",
+};
+
+#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 (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* 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, type)) return 1;
+    if (!type_is_numeric(type)) return 0;
+
+    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 = type;
+                    return 1;
+                }
+                switch (type->Basic.size) {
+                    case 1: if (value <= 255) {
+                                num->type = type;
+                                return 1;
+                            }
+                    case 2: if (value <= 65535) {
+                                num->type = type;
+                                return 1;
+                            }
+                    case 4: if (value <= 4294967295) {
+                                num->type = type;
+                                return 1;
+                            }
+                }
+                
+                onyx_report_error(num->token->pos, "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 = type;
+                                return 1;
+                            } break;
+                    case 2: if (-32768ll <= value && value <= 32767ll) {
+                                num->value.i = (i32) value;
+                                num->type = type;
+                                return 1;
+                            } break;
+                    case 4: if (-2147483648ll <= value && value <= 2147483647ll) {
+                                num->value.i = (i32) value;
+                                num->type = type;
+                                return 1;
+                            } break;
+                    case 8: {   num->type = type;
+                                return 1;
+                            } break;
+                }
+
+                onyx_report_error(num->token->pos, "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, "Integer '%l' does not fit in 32-bit float exactly.", num->value.l);
+                    return 0;
+                }
+
+                num->type = 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, "Integer '%l' does not fit in 64-bit float exactly.", num->value.l);
+                    return 0;
+                }
+
+                num->type = 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 = 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 = type;
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+// NOTE: Returns 0 if it was not possible to make the types compatible.
+b32 type_check_or_auto_cast(AstTyped** pnode, Type* type) {
+    AstTyped* node = *pnode;
+    if (type == NULL) return 0;
+    if (node == NULL) return 0;
+
+    // if (node_is_type((AstNode *) node)) return 0;
+
+    if (node->kind == Ast_Kind_Struct_Literal && node->type_node == NULL) {
+        if (node->entity != NULL) return 1;
+        if (type->kind == Type_Kind_VarArgs) type = type->VarArgs.ptr_to_data->Pointer.elem;
+        if (!type_is_sl_constructable(type)) return 0;
+
+        node->type = type;
+
+        add_entities_for_node(NULL, (AstNode *) node, NULL, NULL);
+        return 1;
+    }
+
+    if (node->kind == Ast_Kind_Array_Literal && node->type_node == NULL) {
+        if (node->entity != NULL) return 1;
+        node->type = type;
+        node->flags |= Ast_Flag_Array_Literal_Typed;
+
+        add_entities_for_node(NULL, (AstNode *) node, NULL, NULL);
+        return 1;
+    }
+
+    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) return 0;
+
+        *pnode = (AstTyped *) resolved;
+        return 1;
+    }
+
+    if (node->kind == Ast_Kind_Overloaded_Function) {
+        AstTyped* func = find_matching_overload_by_type(((AstOverloadedFunction *) node)->overloads, type);
+        if (func == NULL) return 0;
+
+        // 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((AstPolyProc *) node, PPLM_By_Function_Type, type, node->token);
+        if (func == NULL) return 0;
+
+        *pnode = (AstTyped *) func;
+        node = *pnode;
+    }
+
+    // HACK: NullProcHack
+    if (type->kind == Type_Kind_Function && (node->flags & Ast_Flag_Proc_Is_Null) != 0) return 1;
+
+    if (types_are_compatible(node->type, type)) return 1;
+    // :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 1;
+    }
+    if (node_is_auto_cast((AstNode *) node)) {
+        char* dummy;
+        Type* from_type = ((AstUnaryOp *) node)->expr->type;
+        if (!from_type || !cast_is_legal(from_type, type, &dummy)) {
+            return 0;
+
+        } else {
+            ((AstUnaryOp *) node)->type = type;
+            return 1;
+        }
+    }
+    else if (node->kind == Ast_Kind_NumLit) {
+        if (convert_numlit_to_type((AstNumLit *) node, type)) return 1;
+    }
+    else if (node->kind == Ast_Kind_Compound) {
+        if (type->kind != Type_Kind_Compound) return 0;
+
+        AstCompound* compound = (AstCompound *) node;
+
+        u32 expr_count = bh_arr_length(compound->exprs);
+        if (expr_count != type->Compound.count) return 0;
+
+        fori (i, 0, (i64) expr_count) {
+            if (!type_check_or_auto_cast(&compound->exprs[i], type->Compound.types[i])) return 0;
+        }
+
+        compound->type = type_build_compound_type(context.ast_alloc, compound);
+        
+        return 1;
+    }
+    else if (node->kind == Ast_Kind_If_Expression) {
+        AstIfExpression* if_expr = (AstIfExpression *) node;
+
+        b32 true_success  = type_check_or_auto_cast(&if_expr->true_expr,  type);
+        b32 false_success = type_check_or_auto_cast(&if_expr->false_expr, type);
+
+        if (true_success && false_success) {
+            if_expr->type = type;
+            return 1;
+
+        } else {
+            return 0;
+        }
+    }
+    else if (node->kind == Ast_Kind_Alias) {
+        AstAlias* alias = (AstAlias *) node;
+        return type_check_or_auto_cast(&alias->alias, type);
+    }
+
+    return 0;
+}
+
+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);
+        }
+    }
+
+    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);
+        type_check_or_auto_cast(&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->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) {
+            if (bh_abs(((AstNumLit *) node)->value.l) >= (1ull << 32))
+                convert_numlit_to_type((AstNumLit *) node, &basic_types[Basic_Kind_I64]);
+            else
+                convert_numlit_to_type((AstNumLit *) node, &basic_types[Basic_Kind_I32]);
+        }
+        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) {
+    resolve_expression_type(node);
+
+    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);
+    }
+
+    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);
+    }
+
+    if (node_is_type((AstNode*) node)) {
+        Type* type = type_build_from_ast(context.ast_alloc, (AstType *) node);
+        return type->id;
+    }
+
+    return 0;
+}
+
+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->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;
+    }
+
+    if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_Array) {
+        if (!types_are_compatible(to->Slice.ptr_to_data->Pointer.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_VarArgs) {
+        if (!types_are_compatible(to->Slice.ptr_to_data->Pointer.elem, from->VarArgs.ptr_to_data->Pointer.elem)) {
+            *err_msg = "Variadic argument to slice cast is not valid here because the types are different.";
+            return 0;
+        } else {
+            return 1;
+        }
+    }
+
+    if (from->kind == Type_Kind_Slice || to->kind == Type_Kind_Slice) {
+        *err_msg = "Cannot cast to or from a slice.";
+        return 0;
+    }
+
+    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 bh_aprintf(global_scratch_allocator, "%b", func->name->text, func->name->length);
+    }
+
+    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;
+}
+
+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;
+    }
+}
+
+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->named_values = src->named_values;
+    
+    arguments_ensure_length(dest, 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->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->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--;
+    }
+}
+
+// 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);
+
+    return value != 0;
+}
diff --git a/src/builtins.c b/src/builtins.c
new file mode 100644 (file)
index 0000000..7be8a78
--- /dev/null
@@ -0,0 +1,435 @@
+#include "astnodes.h"
+#include "types.h"
+#include "errors.h"
+#include "utils.h"
+
+AstBasicType basic_type_void      = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_Void]  };
+AstBasicType basic_type_bool      = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_Bool]  };
+AstBasicType basic_type_i8        = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_I8]    };
+AstBasicType basic_type_u8        = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_U8]    };
+AstBasicType basic_type_i16       = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_I16]   };
+AstBasicType basic_type_u16       = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_U16]   };
+AstBasicType basic_type_i32       = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_I32]   };
+AstBasicType basic_type_u32       = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_U32]   };
+AstBasicType basic_type_i64       = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_I64]   };
+AstBasicType basic_type_u64       = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_U64]   };
+AstBasicType basic_type_f32       = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_F32]   };
+AstBasicType basic_type_f64       = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_F64]   };
+AstBasicType basic_type_rawptr    = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_Rawptr] };
+AstBasicType basic_type_type_expr = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, 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, 0,   NULL, NULL, &basic_types[Basic_Kind_Int_Unsized] };
+AstBasicType basic_type_float_unsized = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_Float_Unsized] };
+
+static OnyxToken simd_token = { Token_Type_Symbol, 0, "", { 0 } };
+AstBasicType basic_type_i8x16 = { Ast_Kind_Basic_Type, 0, &simd_token, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_I8X16] };
+AstBasicType basic_type_i16x8 = { Ast_Kind_Basic_Type, 0, &simd_token, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_I16X8] };
+AstBasicType basic_type_i32x4 = { Ast_Kind_Basic_Type, 0, &simd_token, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_I32X4] };
+AstBasicType basic_type_i64x2 = { Ast_Kind_Basic_Type, 0, &simd_token, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_I64X2] };
+AstBasicType basic_type_f32x4 = { Ast_Kind_Basic_Type, 0, &simd_token, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_F32X4] };
+AstBasicType basic_type_f64x2 = { Ast_Kind_Basic_Type, 0, &simd_token, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_F64X2] };
+AstBasicType basic_type_v128  = { Ast_Kind_Basic_Type, 0, &simd_token, NULL, 0,  NULL, 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, 0, NULL, 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 } };
+AstNumLit builtin_heap_start  = { Ast_Kind_NumLit, Ast_Flag_Const, &builtin_heap_start_token, NULL, NULL, (AstType *) &basic_type_rawptr, NULL, 0 };
+AstGlobal builtin_stack_top   = { Ast_Kind_Global, Ast_Flag_Const | Ast_Flag_Global_Stack_Top,  &builtin_stack_top_token, NULL, NULL, (AstType *) &basic_type_rawptr, NULL };
+
+AstType  *builtin_string_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;
+
+AstTyped *type_table_node = 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 },
+
+    { NULL, NULL, NULL },
+};
+
+bh_table(OnyxIntrinsic) 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 },
+    { "__zero_value", ONYX_INTRINSIC_ZERO_VALUE },
+
+    { "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 },
+
+    { 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);
+            assert(p);
+
+            symbol_builtin_introduce(p->scope, bsym->sym, bsym->node);
+        }
+        bsym++;
+    }
+
+    Package* p = package_lookup_or_create("builtin", context.global_scope, a);
+
+    builtin_string_type = (AstType *) symbol_raw_resolve(p->scope, "str");
+    if (builtin_string_type == NULL) {
+        onyx_report_error((OnyxFilePos) { 0 }, "'str' struct 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 }, "'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 }, "'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 }, "'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 }, "'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 }, "'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 }, "'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 }, "'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 }, "'Code' struct not found in builtin package.");
+        return;
+    }
+
+    p = package_lookup("builtin.type_info");
+    if (p != NULL) {
+        type_table_node = (AstTyped *) symbol_raw_resolve(p->scope, "type_table");
+    }
+
+    fori (i, 0, Binary_Op_Count) {
+        bh_arr_new(global_heap_allocator, operator_overloads[i], 4); 
+    }
+
+    bh_table_init(global_heap_allocator, intrinsic_table, 128);
+    IntrinsicMap* intrinsic = &builtin_intrinsics[0];
+    while (intrinsic->name != NULL) {
+        bh_table_put(OnyxIntrinsic, intrinsic_table, intrinsic->name, intrinsic->intrinsic);
+        intrinsic++;
+    }
+}
+
+void introduce_build_options(bh_allocator a) {
+    Package* p = package_lookup_or_create("runtime", context.global_scope, a);
+
+    AstNumLit* runtime_type = make_int_literal(a, context.options->runtime);
+    symbol_builtin_introduce(p->scope, "Runtime", (AstNode *) runtime_type);
+}
diff --git a/src/checker.c b/src/checker.c
new file mode 100644 (file)
index 0000000..0e595ae
--- /dev/null
@@ -0,0 +1,2370 @@
+#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, msg); \
+        return Check_Error; \
+    } else { \
+        return Check_Yield_Macro; \
+    } \
+    } while (0)
+
+#define YIELD_(loc, msg, ...) do { \
+    if (context.cycle_detected) { \
+        onyx_report_error(loc, msg, __VA_ARGS__); \
+        return Check_Error; \
+    } else { \
+        return Check_Yield_Macro; \
+    } \
+    } while (0)
+
+#define ERROR(loc, msg) do { \
+    onyx_report_error(loc, msg); \
+    return Check_Error; \
+    } while (0)
+
+#define ERROR_(loc, msg, ...) do { \
+    onyx_report_error(loc, msg, __VA_ARGS__); \
+    return Check_Error; \
+    } while (0)
+
+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_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* aof);
+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_function_header(AstFunction* func);
+CheckStatus check_memres_type(AstMemRes* memres);
+CheckStatus check_memres(AstMemRes* memres);
+CheckStatus check_type(AstType* type);
+CheckStatus check_insert_directive(AstDirectiveInsert** pinsert);
+CheckStatus check_do_block(AstDoBlock** pdoblock);
+
+// HACK HACK HACK
+b32 expression_types_must_be_known = 0;
+
+#define STATEMENT_LEVEL 1
+#define EXPRESSION_LEVEL 2
+u32 current_checking_level=0;
+
+static inline void fill_in_type(AstTyped* node);
+
+static inline void fill_in_array_count(AstType* type_node) {
+    if (type_node == NULL) return;
+
+    if (type_node->kind == Ast_Kind_Type_Alias) {
+        fill_in_array_count(((AstTypeAlias *) type_node)->to);
+    }
+
+    if (type_node->kind == Ast_Kind_Array_Type) {
+        if (((AstArrayType *) type_node)->count_expr) {
+            // CLEANUP: The return value is not checked on this call.
+            check_expression(&((AstArrayType *) type_node)->count_expr);
+
+            resolve_expression_type(((AstArrayType *) type_node)->count_expr);
+        }
+    }
+}
+
+static inline void fill_in_poly_call_args(AstType* type_node) {
+    if (type_node == NULL) return;
+    if (type_node->kind != Ast_Kind_Poly_Call_Type) return;
+
+    AstPolyCallType* pctype = (AstPolyCallType *) type_node;
+
+    bh_arr_each(AstNode *, param, pctype->params) {
+        if (!node_is_type(*param)) {
+            // CLEANUP: The return value is not checked on this call.
+            check_expression((AstTyped **) param);
+            
+            resolve_expression_type((AstTyped *) *param);
+            fill_in_type((AstTyped *) *param);
+        }
+    }
+}
+
+static inline void fill_in_type(AstTyped* node) {
+    fill_in_array_count(node->type_node);
+    fill_in_poly_call_args(node->type_node);
+
+    if (node->type == NULL)
+        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(retnode->token->pos, "Trying to determine automatic return type.");
+
+            *expected_return_type = retnode->expr->type;
+            return Check_Success;
+        }
+
+        if (!type_check_or_auto_cast(&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));
+        }
+
+    } 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);
+
+        } else {
+            if (ifnode->false_stmt != NULL) CHECK(statement, (AstNode **) &ifnode->false_stmt);
+        }
+
+    } 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) CHECK(statement, (AstNode **) &whilenode->false_stmt);
+
+    return Check_Success;
+}
+
+CheckStatus check_for(AstFor* fornode) {
+    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.");
+
+    fornode->loop_type = For_Loop_Invalid;
+    if (types_are_compatible(iter_type, &basic_types[Basic_Kind_I32])) {
+        if (fornode->by_pointer) {
+            ERROR(fornode->var->token->pos, "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(fornode->var->token->pos, "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 = iter_type->Slice.ptr_to_data;
+        else                     fornode->var->type = iter_type->Slice.ptr_to_data->Pointer.elem;
+
+        fornode->loop_type = For_Loop_Slice;
+
+    }
+    else if (iter_type->kind == Type_Kind_VarArgs) {
+        if (fornode->by_pointer) {
+            ERROR_(fornode->var->token->pos, "Cannot iterate by pointer over '%s'.", type_get_name(iter_type));
+        }
+
+        fornode->var->type = iter_type->VarArgs.ptr_to_data->Pointer.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 = iter_type->DynArray.ptr_to_data;
+        else                     fornode->var->type = iter_type->DynArray.ptr_to_data->Pointer.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(fornode->var->token->pos, "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_(fornode->iter->token->pos,
+                "Cannot iterate over a '%s'.",
+                type_get_name(iter_type));
+    }
+
+    CHECK(block, fornode->stmt);
+
+    return Check_Success;
+}
+
+static b32 add_case_to_switch_statement(AstSwitch* switchnode, u64 case_value, AstBlock* block, OnyxFilePos pos) {
+    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, "Multiple cases for values '%d'.", case_value);
+        return 1;
+    }
+
+    bh_imap_put(&switchnode->case_map, case_value, (u64) block);
+    return 0;
+}
+
+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 (!type_is_integer(switchnode->expr->type) && switchnode->expr->type->kind != Type_Kind_Enum) {
+        ERROR(switchnode->expr->token->pos, "expected integer or enum type for switch expression");
+    }
+
+    // LEAK if this has to be yielded
+    bh_imap_init(&switchnode->case_map, global_heap_allocator, bh_arr_length(switchnode->cases) * 2);
+
+    switchnode->min_case = 0xffffffffffffffff;
+
+    // Umm, this doesn't check the type of the case expression to the type of the expression
+    bh_arr_each(AstSwitchCase, sc, switchnode->cases) {
+        CHECK(block, sc->block);
+
+        bh_arr_each(AstTyped *, value, sc->values) {
+            CHECK(expression, value);
+
+            if ((*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;
+            }
+
+            if (!type_check_or_auto_cast(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));
+            }
+
+            if (node_is_type((AstNode*) (*value))) {
+                Type* type = type_build_from_ast(context.ast_alloc, (AstType*) (*value));
+
+                if (add_case_to_switch_statement(switchnode, type->id, sc->block, sc->block->token->pos))
+                    return Check_Error;
+
+                continue;
+            }
+
+            if ((*value)->kind == Ast_Kind_Enum_Value) {
+                (*value) = (AstTyped *) ((AstEnumValue *) (*value))->value;
+            }
+
+            if ((*value)->kind != Ast_Kind_NumLit) {
+                ERROR((*value)->token->pos, "case statement expected compile time known integer");
+            }
+
+            resolve_expression_type((*value));
+            // promote_numlit_to_larger((AstNumLit *) (*value));
+
+            if (add_case_to_switch_statement(switchnode, ((AstNumLit *) (*value))->value.l, sc->block, sc->block->token->pos))
+                return Check_Error;
+        }
+    }
+
+    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 i32 non_baked_argument_count(Arguments* args) {
+    i32 count = 0;
+
+    bh_arr_each(AstTyped *, actual, args->values) {
+        assert((*actual)->kind == Ast_Kind_Argument);
+        if (!((AstArgument *) (*actual))->is_baked) count++;
+    }
+
+    bh_arr_each(AstNamedValue *, named_value, args->named_values) {
+        assert((*named_value)->value->kind == Ast_Kind_Argument);
+        if (!((AstArgument *) (*named_value)->value)->is_baked) count++;
+    }
+
+    return count;
+}
+
+static CheckStatus check_resolve_callee(AstCall* call, AstTyped** effective_callee) {
+    if (call->kind == Ast_Kind_Intrinsic_Call) return Check_Success;
+
+    call->callee = (AstTyped *) strip_aliases((AstNode *) call->callee);
+
+    AstTyped* callee = call->callee;
+    b32 calling_a_macro = 0;
+
+    if (callee->kind == Ast_Kind_Overloaded_Function) {
+        b32 should_yield = 0;
+        AstTyped* new_callee = find_matching_overload_by_arguments(
+            ((AstOverloadedFunction *) callee)->overloads,
+            &call->args,
+            &should_yield);
+
+        if (new_callee == NULL) {
+            if (callee->entity->state > Entity_State_Check_Types && !should_yield) {
+                report_unable_to_match_overload(call);
+                return Check_Error;
+
+            } else {
+                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);
+        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 if (callee->kind == Ast_Kind_Polymorphic_Proc) {
+        AstTyped* new_callee = (AstTyped *) polymorphic_proc_lookup((AstPolyProc *) 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;
+    }
+
+    if (!calling_a_macro) call->callee = 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 (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;
+}
+
+typedef enum ArgState {
+    AS_Expecting_Exact,
+    AS_Expecting_Typed_VA,
+    AS_Expecting_Untyped_VA,
+} ArgState;
+
+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->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;
+
+    // SPEED CLEANUP: Keeping an original copy for basically no reason except that sometimes you
+    // need to know the baked argument values in code generation.
+    // This should only be done once, but right now it is being done everytime this is checked,
+    // which can be multiple if we have to yield on a callee's type.
+    arguments_clone(&call->original_args, &call->args);
+
+    AstFunction* callee=NULL;
+    CHECK(resolve_callee, call, (AstTyped **) &callee);
+
+    // CLEANUP maybe make function_get_expected_arguments?
+    i32 non_vararg_param_count = (i32) callee->type->Function.param_count;
+    if (non_vararg_param_count > 0 &&
+        callee->type->Function.params[callee->type->Function.param_count - 1] == builtin_vararg_type_type)
+        non_vararg_param_count--;
+
+    i32 arg_count = bh_max(non_vararg_param_count, non_baked_argument_count(&call->args));
+    arguments_ensure_length(&call->args, arg_count);
+
+    char* err_msg = NULL;
+    fill_in_arguments(&call->args, (AstNode *) callee, &err_msg);
+    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->addr  = 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->flags & Ast_Flag_Intrinsic) {
+        call->kind = Ast_Kind_Intrinsic_Call;
+        call->callee = NULL;
+
+        token_toggle_end(callee->intrinsic_name);
+        char* intr_name = callee->intrinsic_name->text;
+
+        if (bh_table_has(OnyxIntrinsic, intrinsic_table, intr_name)) {
+            call->intrinsic = bh_table_get(OnyxIntrinsic, intrinsic_table, intr_name);
+
+        } else {
+            onyx_report_error(callee->token->pos, "Intrinsic not supported, '%s'.", intr_name);
+            token_toggle_end(callee->intrinsic_name);
+            return Check_Error;
+        }
+
+        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.");
+    }
+
+    Type **formal_params = callee->type->Function.params;
+
+    Type* variadic_type = NULL;
+    AstParam* variadic_param = NULL;
+
+    // SPEED CLEANUP: Caching the any type here.
+    Type* any_type = type_build_from_ast(context.ast_alloc, builtin_any_type);
+
+    ArgState arg_state = AS_Expecting_Exact;
+    u32 arg_pos = 0;
+    while (1) {
+        switch (arg_state) {
+            case AS_Expecting_Exact: {
+                if (arg_pos >= callee->type->Function.param_count) goto type_checking_done;
+
+                if (formal_params[arg_pos]->kind == Type_Kind_VarArgs) {
+                    variadic_type = formal_params[arg_pos]->VarArgs.ptr_to_data->Pointer.elem;
+                    variadic_param = &callee->params[arg_pos];
+                    arg_state = AS_Expecting_Typed_VA;
+                    continue;
+                }
+
+                if ((i16) arg_pos == callee->type->Function.vararg_arg_pos) {
+                    arg_state = AS_Expecting_Untyped_VA;
+                    continue;
+                }
+
+                if (arg_pos >= (u32) bh_arr_length(arg_arr)) goto type_checking_done;
+                if (!type_check_or_auto_cast(&arg_arr[arg_pos]->value, formal_params[arg_pos])) {
+                    ERROR_(arg_arr[arg_pos]->token->pos,
+                            "The procedure '%s' expects a value of type '%s' for %d%s parameter, got '%s'.",
+                            get_function_name(callee),
+                            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));
+                }
+
+                arg_arr[arg_pos]->va_kind = VA_Kind_Not_VA;
+                break;
+            }
+
+            case AS_Expecting_Typed_VA: {
+                if (arg_pos >= (u32) bh_arr_length(arg_arr)) goto type_checking_done;
+
+                if (variadic_type->id == any_type->id) {
+                    call->va_kind = VA_Kind_Any;
+
+                    resolve_expression_type(arg_arr[arg_pos]->value);
+                    arg_arr[arg_pos]->va_kind = VA_Kind_Any; 
+                    break;
+                }
+
+                call->va_kind = VA_Kind_Typed;
+
+                if (!type_check_or_auto_cast(&arg_arr[arg_pos]->value, variadic_type)) {
+                    onyx_report_error(arg_arr[arg_pos]->token->pos,
+                            "The procedure '%s' expects a value of type '%s' for the variadic parameter, '%b', got '%s'.",
+                            get_function_name(callee),
+                            type_get_name(variadic_type),
+                            variadic_param->local->token->text,
+                            variadic_param->local->token->length,
+                            node_get_type_name(arg_arr[arg_pos]->value));
+                    return Check_Error;
+                }
+
+                arg_arr[arg_pos]->va_kind = VA_Kind_Typed;
+                break;
+            }
+
+            case AS_Expecting_Untyped_VA: {
+                call->va_kind = VA_Kind_Untyped;
+
+                if (arg_pos >= (u32) bh_arr_length(arg_arr)) goto type_checking_done;
+
+                resolve_expression_type(arg_arr[arg_pos]->value);
+                if (arg_arr[arg_pos]->value->type == NULL) {
+                    ERROR(arg_arr[arg_pos]->token->pos, "Unable to resolve type for argument.");
+                }
+
+                arg_arr[arg_pos]->va_kind = VA_Kind_Untyped;
+                break;
+            }
+        }
+
+        arg_pos++;
+    }
+
+type_checking_done:
+
+    call->flags |= Ast_Flag_Has_Been_Checked;
+
+    if (arg_pos < callee->type->Function.needed_param_count)
+        ERROR(call->token->pos, "Too few arguments to function call.");
+
+    if (arg_pos < (u32) arg_count)
+        ERROR(call->token->pos, "Too many arguments to function call.");
+
+    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, "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) {
+    if (bh_arr_length(operator_overloads[binop->operation]) == 0) return NULL;
+
+    Arguments args = ((Arguments) { NULL, NULL });
+    bh_arr_new(global_heap_allocator, args.values, 2);
+    bh_arr_push(args.values, binop->left);
+    bh_arr_push(args.values, binop->right);
+
+    if (binop_is_assignment(binop->operation)) {
+        args.values[0] = (AstTyped *) make_address_of(context.ast_alloc, binop->left);
+
+        u32 current_checking_level_store = current_checking_level;
+        CheckStatus cs = check_address_of((AstAddressOf *) args.values[0]);
+        current_checking_level = current_checking_level_store;
+
+        if (cs == Check_Yield_Macro) return (AstCall *) &node_that_signals_a_yield;
+        if (cs == Check_Error) {
+            return NULL;
+        }
+    }
+
+    b32 should_yield = 0;
+    AstTyped* overload = find_matching_overload_by_arguments(operator_overloads[binop->operation], &args, &should_yield);
+    if (should_yield) {
+        bh_arr_free(args.values);
+        return (AstCall *) &node_that_signals_a_yield;
+    }
+
+    if (overload == NULL) {
+        bh_arr_free(args.values);
+        return NULL;
+    }
+
+    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;
+
+    bh_arr_each(AstTyped *, arg, args.values)
+        *arg = (AstTyped *) make_argument(context.ast_alloc, *arg);
+
+    implicit_call->args = 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) {
+            resolve_expression_type(binop->right);
+
+            if (binop->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 (binop->right->type->kind == Type_Kind_Compound) {
+                AstCompound* lhs = (AstCompound *) binop->left;
+                if (lhs->kind != Ast_Kind_Compound) {
+                    ERROR_(binop->token->pos,
+                            "Expected left hand side to have %d expressions.",
+                            binop->right->type->Compound.count);
+                }
+
+                i32 expr_count = binop->right->type->Compound.count;
+                if (bh_arr_length(lhs->exprs) != expr_count) {
+                    ERROR_(binop->token->pos,
+                            "Expected left hand side to have %d expressions.",
+                            binop->right->type->Compound.count);
+                }
+
+                fori (i, 0, expr_count) {
+                    lhs->exprs[i]->type = binop->right->type->Compound.types[i];
+                }
+
+                lhs->type = type_build_compound_type(context.ast_alloc, lhs);
+
+            } else {
+                binop->left->type = binop->right->type;
+            }
+        }
+
+    } else {
+        // NOTE: +=, -=, ...
+
+        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.");
+        }
+    }
+
+    if (!type_check_or_auto_cast(&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->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.");
+        else if (type_check_or_auto_cast(&binop->left, rtype));
+        else if (type_check_or_auto_cast(&binop->right, ltype));
+        else {
+            ERROR_(binop->token->pos,
+                    "Cannot compare '%s' to '%s'.",
+                    type_get_name(ltype),
+                    type_get_name(rtype));
+        }
+    }
+
+    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;
+
+    u32 current_checking_level_store = current_checking_level;
+    CHECK(expression, &binop->left);
+    CHECK(expression, &binop->right);
+    current_checking_level = current_checking_level_store;
+
+    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.");
+        }
+    }
+
+    // :UnaryFieldAccessIsGross
+    if (binop->left->kind == Ast_Kind_Unary_Field_Access || binop->right->kind == Ast_Kind_Unary_Field_Access) {
+        if      (type_check_or_auto_cast(&binop->left, binop->right->type));
+        else if (type_check_or_auto_cast(&binop->right, binop->left->type));
+        else {
+            report_bad_binaryop(binop);
+            return Check_Error;
+        }
+    }
+
+    // NOTE: Try operator overloading before checking everything else.
+    if ((binop->left->type != NULL && binop->right->type != NULL) &&
+        (binop->left->type->kind != Type_Kind_Basic || binop->right->type->kind != Type_Kind_Basic)) {
+        AstCall *implicit_call = binaryop_try_operator_overload(binop);
+
+        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);
+
+    // 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.");
+        }
+        else if (type_check_or_auto_cast(&binop->left, binop->right->type));
+        else if (type_check_or_auto_cast(&binop->right, binop->left->type));
+        else {
+            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];
+    }
+
+    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) return Check_Success;
+
+        if (!node_is_type((AstNode *) sl->stnode)) {
+            ERROR(sl->token->pos, "Type used for struct literal is not a type.");
+        }
+
+        fill_in_type((AstTyped *) sl);
+        if (sl->type == NULL)
+            YIELD(sl->token->pos, "Trying to resolve type of struct literal.");
+    }
+
+    if (!type_is_structlike_strict(sl->type)) {
+        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)) {
+            onyx_report_error(sl->token->pos, 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,
+                        "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);
+
+        // HACK HACK HACK
+        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 '%s'.", smem.name);
+        }
+
+        if (!type_check_or_auto_cast(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)
+            YIELD(al->token->pos, "Waiting for array literal type to be known.");
+
+        if (!node_is_type((AstNode *) al->atnode))
+            ERROR(al->token->pos, "Array type is not a type.");
+
+        fill_in_type((AstTyped *) al);
+        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;
+
+    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);
+
+        if (!type_check_or_auto_cast(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;
+    CHECK(expression, &range->low);
+    CHECK(expression, &range->high);
+
+    Type* expected_range_type = builtin_range_type_type;
+    StructMember smem;
+
+    type_lookup_member(expected_range_type, "low", &smem);
+    if (!type_check_or_auto_cast(&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);
+    if (!type_check_or_auto_cast(&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;
+    }
+
+    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);
+
+    if (!type_check_or_auto_cast(&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) {
+        // ERROR(doblock->token->pos, "Unable to determine type of do-block expression.");
+        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* aof) {
+    CHECK(expression, &aof->expr);
+    if (aof->expr->type == NULL) {
+        YIELD(aof->token->pos, "Trying to resolve type of expression to take a reference.");
+    }
+
+    if ((aof->expr->kind != Ast_Kind_Subscript
+            && aof->expr->kind != Ast_Kind_Dereference
+            && aof->expr->kind != Ast_Kind_Field_Access
+            && aof->expr->kind != Ast_Kind_Memres
+            && aof->expr->kind != Ast_Kind_Local)
+            || (aof->expr->flags & Ast_Flag_Cannot_Take_Addr) != 0) {
+        ERROR(aof->token->pos, "Cannot take the address of something that is not an l-value.");
+    }
+
+    aof->expr->flags |= Ast_Flag_Address_Taken;
+
+    aof->type = type_make_pointer(context.ast_alloc, aof->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);
+
+    // NOTE: Try operator overloading before checking everything else.
+    if ((sub->addr->type != NULL && 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);
+
+        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 (types_are_compatible(sub->expr->type, builtin_range_type_type)) {
+        Type *of = NULL;
+        if (sub->addr->type->kind == Type_Kind_Pointer)
+            of = sub->addr->type->Pointer.elem;
+        else if (sub->addr->type->kind == Type_Kind_Array)
+            of = sub->addr->type->Array.elem;
+        else {
+            // 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 (sub->expr->type->kind != Type_Kind_Basic
+            || (sub->expr->type->Basic.kind != Basic_Kind_I32 && sub->expr->type->Basic.kind != Basic_Kind_U32)) {
+        report_bad_binaryop((AstBinaryOp *) sub);
+        ERROR_(sub->token->pos,
+            "Expected type u32 or i32 for index, got '%s'.",
+            node_get_type_name(sub->expr));
+    }
+
+    if (sub->addr->type->kind == Type_Kind_Pointer)
+        sub->type = sub->addr->type->Pointer.elem;
+    else if (sub->addr->type->kind == Type_Kind_Array)
+        sub->type = sub->addr->type->Array.elem;
+    else 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;
+        sub->type = sub->addr->type->Pointer.elem;
+    }
+    else {
+        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;
+    CHECK(expression, &field->expr);
+    if (field->expr->type == NULL) {
+        // onyx_report_error(field->token->pos, "Unable to deduce type of expression for accessing field.");
+        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;
+    }
+
+    StructMember smem;
+    if (field->token != NULL && field->field == NULL) {
+        token_toggle_end(field->token);
+        // CLEANUP: Duplicating the string here isn't the best for effiency,
+        // but it fixes a lot of bugs, so here we are.
+        //                                      - brendanfh  2020/12/08
+        field->field = bh_strdup(context.ast_alloc, field->token->text);
+        token_toggle_end(field->token);
+    }
+
+    if (!type_lookup_member(field->expr->type, field->field, &smem)) {
+        AstType* type_node = field->expr->type->ast_type;
+        AstNode* n = try_symbol_raw_resolve_from_node((AstNode *) type_node, field->field);
+        if (n) {
+            *pfield = (AstFieldAccess *) n;
+            return Check_Success;
+        }
+
+        ERROR_(field->token->pos,
+            "Field '%s' does not exists on '%s'.",
+            field->field,
+            node_get_type_name(field->expr));
+    }
+
+    field->offset = smem.offset;
+    field->idx = smem.idx;
+    field->type = smem.type;
+
+    return Check_Success;
+}
+
+CheckStatus check_method_call(AstBinaryOp** mcall) {
+    CHECK(expression, &(*mcall)->left);
+    if ((*mcall)->left->type == NULL) {
+        YIELD((*mcall)->token->pos, "Trying to resolve type of left hand side.");
+    }
+
+    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)
+            implicit_argument = (AstTyped *) make_address_of(context.ast_alloc, implicit_argument);
+
+        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;
+    }
+    (*mcall)->flags |= Ast_Flag_Has_Been_Checked;
+
+    CHECK(call, &call_node);
+    call_node->next = (*mcall)->next;
+
+    *mcall = (AstBinaryOp *) call_node;
+    return Check_Success;
+}
+
+CheckStatus check_size_of(AstSizeOf* so) {
+    fill_in_array_count(so->so_ast_type);
+    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);
+
+    return Check_Success;
+}
+
+CheckStatus check_align_of(AstAlignOf* ao) {
+    fill_in_array_count(ao->ao_ast_type);
+    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);
+
+    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.
+        if (expr->kind == Ast_Kind_Typeof) {
+            CHECK(type, (AstType *) expr);
+        }
+
+        // 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.");
+            }
+        }
+
+        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;
+    }
+
+    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_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:
+            onyx_report_error(expr->token->pos,
+                    "Symbol was unresolved in symbol resolution phase, '%b'. This is definitely a compiler bug.",
+                    expr->token->text, expr->token->length);
+            retval = Check_Error;
+            break;
+
+        case Ast_Kind_Param:
+            if (expr->type == NULL) {
+                onyx_report_error(expr->token->pos, "Parameter with bad type.");
+                retval = Check_Error;
+            }
+            break;
+
+        case Ast_Kind_Local: break;
+
+        case Ast_Kind_Address_Of:    retval = check_address_of((AstAddressOf *) expr); 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, "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:
+            *pexpr = (AstTyped *) ((AstDirectiveSolidify *) expr)->resolved_proc;
+            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_StrLit: break;
+        case Ast_Kind_File_Contents: break;
+        case Ast_Kind_Overloaded_Function: break;
+        case Ast_Kind_Enum_Value: break;
+        case Ast_Kind_Memres: break;
+        case Ast_Kind_Polymorphic_Proc: break;
+        case Ast_Kind_Package: break;
+        case Ast_Kind_Error: break;
+        case Ast_Kind_Unary_Field_Access: break;
+
+        // NOTE: The only way to have an Intrinsic_Call node is to have gone through the
+        // checking of a call node at least once.
+        case Ast_Kind_Intrinsic_Call: break;
+
+        default:
+            retval = Check_Error;
+            onyx_report_error(expr->token->pos, "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);
+
+    if (!type_check_or_auto_cast(&insert->code_expr, code_type)) {
+        ERROR_(insert->token->pos, "#insert 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_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_Call: {
+            CHECK(call, (AstCall **) pstmt);
+            stmt->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.");
+            }
+            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) {
+    CHECK(statement_chain, &block->body);
+
+    // CLEANUP: There will need to be some other method of 
+    // checking the following conditions.
+    //
+    // bh_arr_each(AstTyped *, value, block->allocate_exprs) {
+    //     fill_in_type(*value);
+
+    //     if ((*value)->kind == Ast_Kind_Local) {
+    //         if ((*value)->type == NULL) {
+    //             onyx_report_error((*value)->token->pos,
+    //                     "Unable to resolve type for local '%b'.",
+    //                     (*value)->token->text, (*value)->token->length);
+    //             return Check_Error;
+    //         }
+
+    //         if ((*value)->type->kind == Type_Kind_Compound) {
+    //             onyx_report_error((*value)->token->pos,
+    //                     "Compound type not allowed as local variable type. Try splitting this into multiple variables.");
+    //             return Check_Error;
+    //         }
+    //     }
+    // }
+
+    return Check_Success;
+}
+
+CheckStatus check_function(AstFunction* func) {
+    if (func->flags & Ast_Flag_Already_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");
+    
+    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) return status;
+    }
+
+    if (*expected_return_type == &type_auto_return) {
+        *expected_return_type = &basic_types[Basic_Kind_Void];
+    }
+
+    func->flags |= Ast_Flag_Already_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, "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.");
+
+    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 stcache
+    type_build_from_ast(context.ast_alloc, (AstType *) s_node);
+    if (s_node->stcache == NULL || !s_node->stcache_is_valid)
+        YIELD(s_node->token->pos, "Waiting for type to be constructed.");
+
+    bh_arr_each(StructMember *, smem, s_node->stcache->Struct.memarr) {
+        if ((*smem)->type->kind == Type_Kind_Compound) {
+            ERROR(s_node->token->pos, "Compound types are not allowed as struct member types.");
+        }
+    }
+
+    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.");
+
+    bh_arr_each(StructMember *, smem, s_node->stcache->Struct.memarr) {
+        if ((*smem)->initial_value && *(*smem)->initial_value) {
+            CHECK(expression, (*smem)->initial_value);
+
+            if (!type_check_or_auto_cast((*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);
+        }
+    }
+
+    return Check_Success;
+}
+
+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;
+
+    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) CHECK(type, local->type_node);
+        if (local->type_node != NULL) {
+            if (!node_is_type((AstNode *) local->type_node)) {
+                ERROR(local->token->pos, "Parameter type is not a type.");
+            }
+        }
+
+        fill_in_type((AstTyped *) local);
+        if (local->type == NULL) {
+            // onyx_report_error(param->local->token->pos,
+            //         "Unable to resolve type for parameter, '%b'",
+            //         local->token->text,
+            //         local->token->length);
+            YIELD(local->token->pos, "Waiting for parameter type to be known.");
+        }
+
+        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 (!type_check_or_auto_cast(&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) {
+    if (memres->initial_value != NULL) {
+        CHECK(expression, &memres->initial_value);
+        resolve_expression_type(memres->initial_value);
+
+        if ((memres->initial_value->flags & Ast_Flag_Comptime) == 0) {
+            ERROR(memres->initial_value->token->pos, "Top level expressions must be compile time known.");
+        }
+
+        if (memres->type != NULL) {
+            Type* memres_type = memres->type;
+            if (!type_check_or_auto_cast(&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 {
+            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;
+        }
+    }
+
+    return Check_Success;
+}
+
+CheckStatus check_type(AstType* type) {
+    if (type == NULL) return Check_Success;
+    
+    while (type->kind == Ast_Kind_Type_Alias)
+        type = ((AstTypeAlias *) type)->to;
+
+    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);
+                }
+            }
+
+            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: CHECK(type, ((AstPointerType *) type)->elem); break;
+        case Ast_Kind_Slice_Type:   CHECK(type, ((AstSliceType *) type)->elem); break;
+        case Ast_Kind_DynArr_Type:  CHECK(type, ((AstDynArrType *) type)->elem); break;
+        case Ast_Kind_VarArg_Type:  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;
+        }    
+    }
+
+    return Check_Success;
+}
+
+CheckStatus check_static_if(AstIf* static_if) {
+    expression_types_must_be_known = 1;
+    CheckStatus result = check_expression(&static_if->cond);
+    if (result == Check_Yield_Macro) return Check_Yield_Macro;
+    expression_types_must_be_known = 0;
+
+    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) {
+        AstTyped* export = ((AstDirectiveExport *) directive)->export;
+        if (export->entity && export->entity->state <= Entity_State_Check_Types)
+            YIELD(directive->token->pos, "Waiting for export type to be known.");
+    }
+
+    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_node(AstNode* node) {
+    switch (node->kind) {
+        case Ast_Kind_Function:             return check_function((AstFunction *) node);
+        case Ast_Kind_Overloaded_Function:  return check_overloaded_function((AstOverloadedFunction *) node);
+        case Ast_Kind_Block:                return check_block((AstBlock *) node);
+        case Ast_Kind_Return:               return check_return((AstReturn *) node);
+        case Ast_Kind_If:                   return check_if((AstIfWhile *) node);
+        case Ast_Kind_Static_If:            return check_if((AstIfWhile *) node);
+        case Ast_Kind_While:                return check_while((AstIfWhile *) node);
+        case Ast_Kind_Call:                 return check_call((AstCall **) &node);
+        case Ast_Kind_Binary_Op:            return check_binaryop((AstBinaryOp **) &node);
+        default:                            return check_expression((AstTyped **) &node);
+    }
+}
+
+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_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);
+
+        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_Process_Directive:        cs = check_process_directive((AstNode *) ent->expr); break;
+
+        case Entity_Type_File_Contents: 
+            if (context.options->no_file_contents) {
+                onyx_report_error(ent->expr->token->pos, "#file_contents is disabled for this compilation.");
+            }
+            break;
+
+        default: break;
+    }
+
+    if (cs == Check_Success)          ent->state = Entity_State_Code_Gen;
+    if (cs == Check_Complete)         ent->state = Entity_State_Finalized;
+    if (cs == Check_Return_To_Symres) ent->state = Entity_State_Resolve_Symbols;
+    if (cs == Check_Yield_Macro)      ent->macro_attempts++;
+    else {
+        ent->macro_attempts = 0;
+        ent->micro_attempts = 0;
+    }
+}
diff --git a/src/clone.c b/src/clone.c
new file mode 100644 (file)
index 0000000..ac4a893
--- /dev/null
@@ -0,0 +1,496 @@
+#include "astnodes.h"
+#include "parser.h"
+#include "utils.h"
+
+static inline b32 should_clone(AstNode* node) {
+    if (node->flags & Ast_Flag_No_Clone) 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_Enum_Type:
+        case Ast_Kind_Enum_Value:
+        case Ast_Kind_Overloaded_Function:
+        case Ast_Kind_Polymorphic_Proc:
+        case Ast_Kind_Alias:
+        case Ast_Kind_Code_Block:
+        case Ast_Kind_Macro:
+        case Ast_Kind_File_Contents:
+            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_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(AstPolyProc);
+        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_Do_Block: return sizeof(AstDoBlock);
+        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;
+}
+
+#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:
+            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);
+
+            C(AstIfWhile, cond);
+            //fallthrough
+
+        case Ast_Kind_Static_If:
+            C(AstIfWhile, true_stmt);
+            C(AstIfWhile, false_stmt);
+            break;
+
+        case Ast_Kind_Switch: {
+            AstSwitch* dw = (AstSwitch *) nn;
+            AstSwitch* sw = (AstSwitch *) node;
+
+            ((AstSwitch *) nn)->initialization = ast_clone_list(a, ((AstSwitch *) node)->initialization);
+            dw->expr = (AstTyped *) ast_clone(a, sw->expr);
+
+            dw->default_case = (AstBlock *) ast_clone(a, sw->default_case);
+
+            dw->cases = NULL;
+            bh_arr_new(global_heap_allocator, dw->cases, bh_arr_length(sw->cases));
+
+            bh_arr_each(AstSwitchCase, c, sw->cases) {
+                bh_arr(AstTyped *) new_values = NULL;
+                bh_arr_new(global_heap_allocator, new_values, bh_arr_length(c->values));
+                bh_arr_each(AstTyped *, value, c->values)
+                    bh_arr_push(new_values, (AstTyped *) ast_clone(a, *value));
+
+                AstSwitchCase sc;
+                sc.values = new_values; 
+                sc.block = (AstBlock *) ast_clone(a, c->block);
+                bh_arr_push(dw->cases, sc);
+            }
+            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->stcache = NULL;
+            break;
+        }
+
+        case Ast_Kind_Struct_Member:
+            C(AstStructMember, type_node);
+            C(AstStructMember, initial_value);
+            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: {
+            if (clone_depth > 1) {
+                clone_depth--;
+                return node;
+            }
+
+            AstFunction* df = (AstFunction *) nn;
+            AstFunction* sf = (AstFunction *) node;
+
+            if (sf->flags & Ast_Flag_Foreign) return node;
+
+            df->return_type = (AstType *) ast_clone(a, sf->return_type);
+            df->body = (AstBlock *) ast_clone(a, sf->body);
+
+            df->params = NULL;
+            bh_arr_new(global_heap_allocator, df->params, bh_arr_length(sf->params));
+
+            bh_arr_each(AstParam, param, sf->params) {
+                AstParam new_param = { 0 };
+                new_param.local = (AstLocal *) ast_clone(a, param->local);
+                new_param.default_value = (AstTyped *) ast_clone(a, param->default_value);
+                new_param.vararg_kind = param->vararg_kind;
+                bh_arr_push(df->params, new_param);
+            }
+
+            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 = (AstPolyProc *) 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_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;
+    }
+
+    clone_depth--;
+    return nn;
+}
+
+#undef C
+
+AstFunction* clone_function_header(bh_allocator a, AstFunction* func) {
+    if (func->kind != Ast_Kind_Function) return NULL;
+
+    if (func->flags & Ast_Flag_Foreign) return func;
+
+    AstFunction* new_func = onyx_ast_node_new(a, sizeof(AstFunction), func->kind);
+    memmove(new_func, func, sizeof(AstFunction));
+
+    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;
+        new_param.local = (AstLocal *) ast_clone(a, param->local);
+        new_param.default_value = (AstTyped *) ast_clone(a, param->default_value);
+        new_param.vararg_kind = param->vararg_kind;
+        bh_arr_push(new_func->params, new_param);
+    }
+
+    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_Function) return;
+
+    dest->body = (AstBlock *) ast_clone(a, source->body);
+}
diff --git a/src/doc.c b/src/doc.c
new file mode 100644 (file)
index 0000000..b6e06c3
--- /dev/null
+++ b/src/doc.c
@@ -0,0 +1,286 @@
+#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);
+
+    bh_table_each_start(AstNode *, p->scope->symbols)
+        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);
+    bh_table_each_end;
+
+    bh_table_each_start(AstNode *, p->private_scope->symbols)
+        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);
+    bh_table_each_end;
+
+    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);
+
+    bh_table_each_start(Package *, context.packages);
+        DocPackage dp = doc_package_create(value, a);
+        bh_arr_push(doc.package_docs, dp);
+    bh_table_each_end;
+
+    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
new file mode 100644 (file)
index 0000000..ed209a2
--- /dev/null
@@ -0,0 +1,354 @@
+#include "bh.h"
+#include "astnodes.h"
+#include "utils.h"
+
+static inline i32 entity_phase(Entity* e1) {
+    if (e1->state <= Entity_State_Parse) 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) {
+    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;
+
+        eh_shift_down(entities, min_index);
+    }
+}
+
+void entity_heap_init(EntityHeap* entities) {
+    bh_arena_init(&entities->entity_arena, global_heap_allocator, 32 * 1024);
+}
+
+// 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->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);
+}
+
+// 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);          \
+    }                                                           \
+
+
+    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_File: {
+            ent.state = Entity_State_Parse;
+            ent.type = Entity_Type_Load_File;
+            ent.include = (AstInclude *) node;
+            ENTITY_INSERT(ent);
+            break;
+        }
+
+        case Ast_Kind_Load_Path: {
+            ent.state = Entity_State_Parse;
+            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 ((node->flags & Ast_Flag_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: {
+            if ((node->flags & Ast_Flag_Foreign) != 0) {
+                ent.type   = Entity_Type_Foreign_Global_Header;
+                ent.global = (AstGlobal *) node;
+                ENTITY_INSERT(ent);
+
+            } else {
+                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_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);
+
+            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 = (AstPolyProc *) 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: {
+            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;
+        }
+
+        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
new file mode 100644 (file)
index 0000000..0a07452
--- /dev/null
@@ -0,0 +1,117 @@
+#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, ' ', 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[97m");
+}
+
+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
+
+    bh_arr_each(OnyxError, err, errors.errors) {
+        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);
+        }
+    }
+}
+
+b32 onyx_has_errors() {
+    return bh_arr_length(errors.errors) > 0;
+}
+
+void onyx_clear_errors() {
+    bh_arr_set_length(errors.errors, 0);
+}
+
+void onyx_report_error(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,
+        .text = bh_strdup(errors.msg_alloc, msg),
+    };
+
+    bh_arr_push(errors.errors, err);
+}
+
+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,
+        .text = msg,
+    };
+
+    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
new file mode 100644 (file)
index 0000000..a1f1ac3
--- /dev/null
+++ b/src/lex.c
@@ -0,0 +1,504 @@
+#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",
+    "proc",
+    "as",
+    "cast",
+    "while",
+    "for",
+    "break",
+    "continue",
+    "sizeof",
+    "alignof",
+    "typeof",
+    "defer",
+    "do",
+    "switch",
+    "case",
+    "fallthrough",
+    "macro",
+
+    "->",
+    "<-",
+    "---",
+    "|>",
+
+    ">=",
+    "<=",
+    "==",
+    "!=",
+    "+=",
+    "-=",
+    "*=",
+    "/=",
+    "%=",
+    "&=",
+    "|=",
+    "^=",
+    "&&",
+    "||",
+    "<<",
+    ">>",
+    ">>>",
+    "<<=",
+    ">>=",
+    ">>>=",
+    "..",
+    "~~",
+
+    "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;
+    }
+
+    // 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;
+    }
+
+    // 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);
+        LITERAL_TOKEN("as",          1, Token_Type_Keyword_As);
+        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);
+        break;
+    case 'm':
+        LITERAL_TOKEN("macro",       1, Token_Type_Keyword_Macro);
+        break;
+    case 'p':
+        LITERAL_TOKEN("package",     1, Token_Type_Keyword_Package);
+        LITERAL_TOKEN("proc",        1, Token_Type_Keyword_Proc);
+        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);
+        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_Right_Arrow);
+        LITERAL_TOKEN("<<=",         0, Token_Type_Shl_Equal);
+        LITERAL_TOKEN("<<",          0, Token_Type_Shift_Left);
+        LITERAL_TOKEN("<=",          0, Token_Type_Less_Equal);
+        break;
+
+    case '>':
+        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);
+        LITERAL_TOKEN(">=",          0, Token_Type_Greater_Equal);
+        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) || charset_contains("_$", *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;
+}
index 0352896c602ca0ac83016282cfa788778d77b58d..6f1557d2c906eabddeb55cdc86ebe7f4a8447a9e 100644 (file)
@@ -2,12 +2,12 @@
 #define BH_DEFINE
 #include "bh.h"
 
-#include "onyxlex.h"
-#include "onyxerrors.h"
-#include "onyxparser.h"
-#include "onyxutils.h"
-#include "onyxwasm.h"
-#include "onyxdoc.h"
+#include "lex.h"
+#include "errors.h"
+#include "parser.h"
+#include "utils.h"
+#include "wasm.h"
+#include "doc.h"
 
 #define VERSION "v0.1.0-beta"
 
diff --git a/src/onyxastnodes.c b/src/onyxastnodes.c
deleted file mode 100644 (file)
index 666a203..0000000
+++ /dev/null
@@ -1,1046 +0,0 @@
-#include "onyxastnodes.h"
-#include "onyxparser.h"
-#include "onyxutils.h"
-
-static const char* ast_node_names[] = {
-    "ERROR",
-    "PACKAGE",
-    "INCLUDE FILE",
-    "INCLUDE FOLDER",
-    "MEMORY RESERVATION",
-
-    "BINDING",
-    "ALIAS",
-    "FUNCTION",
-    "OVERLOADED_FUNCTION",
-    "POLYMORPHIC PROC",
-    "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",
-    "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",
-    "SWITCH CASE",
-
-    "SOLIDIFY",
-    "STATIC IF",
-    "STATIC ERROR",
-    "ADD OVERLOAD",
-    "OPERATOR OVERLOAD",
-    "EXPORT",
-    "DEFINED",
-    "CALL SITE",
-
-    "CODE BLOCK",
-    "DIRECTIVE INSERT",
-    "MACRO",
-    "DO BLOCK",
-
-    "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",
-};
-
-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",
-    "Type Alias",
-    "Memory Reservation Type",
-    "Use",
-    "Polymorphic Proc",
-    "Macro",
-    "Foreign_Function Header",
-    "Foreign_Global Header",
-    "Function Header",
-    "Global Header",
-    "Process Directive",
-    "Struct Member Default",
-    "Memory Reservation",
-    "Expression",
-    "Global",
-    "Overloaded_Function",
-    "Function",
-};
-
-#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 (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* 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, type)) return 1;
-    if (!type_is_numeric(type)) return 0;
-
-    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 = type;
-                    return 1;
-                }
-                switch (type->Basic.size) {
-                    case 1: if (value <= 255) {
-                                num->type = type;
-                                return 1;
-                            }
-                    case 2: if (value <= 65535) {
-                                num->type = type;
-                                return 1;
-                            }
-                    case 4: if (value <= 4294967295) {
-                                num->type = type;
-                                return 1;
-                            }
-                }
-                
-                onyx_report_error(num->token->pos, "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 = type;
-                                return 1;
-                            } break;
-                    case 2: if (-32768ll <= value && value <= 32767ll) {
-                                num->value.i = (i32) value;
-                                num->type = type;
-                                return 1;
-                            } break;
-                    case 4: if (-2147483648ll <= value && value <= 2147483647ll) {
-                                num->value.i = (i32) value;
-                                num->type = type;
-                                return 1;
-                            } break;
-                    case 8: {   num->type = type;
-                                return 1;
-                            } break;
-                }
-
-                onyx_report_error(num->token->pos, "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, "Integer '%l' does not fit in 32-bit float exactly.", num->value.l);
-                    return 0;
-                }
-
-                num->type = 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, "Integer '%l' does not fit in 64-bit float exactly.", num->value.l);
-                    return 0;
-                }
-
-                num->type = 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 = 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 = type;
-            return 1;
-        }
-    }
-
-    return 0;
-}
-
-// NOTE: Returns 0 if it was not possible to make the types compatible.
-b32 type_check_or_auto_cast(AstTyped** pnode, Type* type) {
-    AstTyped* node = *pnode;
-    if (type == NULL) return 0;
-    if (node == NULL) return 0;
-
-    // if (node_is_type((AstNode *) node)) return 0;
-
-    if (node->kind == Ast_Kind_Struct_Literal && node->type_node == NULL) {
-        if (node->entity != NULL) return 1;
-        if (type->kind == Type_Kind_VarArgs) type = type->VarArgs.ptr_to_data->Pointer.elem;
-        if (!type_is_sl_constructable(type)) return 0;
-
-        node->type = type;
-
-        add_entities_for_node(NULL, (AstNode *) node, NULL, NULL);
-        return 1;
-    }
-
-    if (node->kind == Ast_Kind_Array_Literal && node->type_node == NULL) {
-        if (node->entity != NULL) return 1;
-        node->type = type;
-        node->flags |= Ast_Flag_Array_Literal_Typed;
-
-        add_entities_for_node(NULL, (AstNode *) node, NULL, NULL);
-        return 1;
-    }
-
-    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) return 0;
-
-        *pnode = (AstTyped *) resolved;
-        return 1;
-    }
-
-    if (node->kind == Ast_Kind_Overloaded_Function) {
-        AstTyped* func = find_matching_overload_by_type(((AstOverloadedFunction *) node)->overloads, type);
-        if (func == NULL) return 0;
-
-        // 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((AstPolyProc *) node, PPLM_By_Function_Type, type, node->token);
-        if (func == NULL) return 0;
-
-        *pnode = (AstTyped *) func;
-        node = *pnode;
-    }
-
-    // HACK: NullProcHack
-    if (type->kind == Type_Kind_Function && (node->flags & Ast_Flag_Proc_Is_Null) != 0) return 1;
-
-    if (types_are_compatible(node->type, type)) return 1;
-    // :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 1;
-    }
-    if (node_is_auto_cast((AstNode *) node)) {
-        char* dummy;
-        Type* from_type = ((AstUnaryOp *) node)->expr->type;
-        if (!from_type || !cast_is_legal(from_type, type, &dummy)) {
-            return 0;
-
-        } else {
-            ((AstUnaryOp *) node)->type = type;
-            return 1;
-        }
-    }
-    else if (node->kind == Ast_Kind_NumLit) {
-        if (convert_numlit_to_type((AstNumLit *) node, type)) return 1;
-    }
-    else if (node->kind == Ast_Kind_Compound) {
-        if (type->kind != Type_Kind_Compound) return 0;
-
-        AstCompound* compound = (AstCompound *) node;
-
-        u32 expr_count = bh_arr_length(compound->exprs);
-        if (expr_count != type->Compound.count) return 0;
-
-        fori (i, 0, (i64) expr_count) {
-            if (!type_check_or_auto_cast(&compound->exprs[i], type->Compound.types[i])) return 0;
-        }
-
-        compound->type = type_build_compound_type(context.ast_alloc, compound);
-        
-        return 1;
-    }
-    else if (node->kind == Ast_Kind_If_Expression) {
-        AstIfExpression* if_expr = (AstIfExpression *) node;
-
-        b32 true_success  = type_check_or_auto_cast(&if_expr->true_expr,  type);
-        b32 false_success = type_check_or_auto_cast(&if_expr->false_expr, type);
-
-        if (true_success && false_success) {
-            if_expr->type = type;
-            return 1;
-
-        } else {
-            return 0;
-        }
-    }
-    else if (node->kind == Ast_Kind_Alias) {
-        AstAlias* alias = (AstAlias *) node;
-        return type_check_or_auto_cast(&alias->alias, type);
-    }
-
-    return 0;
-}
-
-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);
-        }
-    }
-
-    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);
-        type_check_or_auto_cast(&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->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) {
-            if (bh_abs(((AstNumLit *) node)->value.l) >= (1ull << 32))
-                convert_numlit_to_type((AstNumLit *) node, &basic_types[Basic_Kind_I64]);
-            else
-                convert_numlit_to_type((AstNumLit *) node, &basic_types[Basic_Kind_I32]);
-        }
-        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) {
-    resolve_expression_type(node);
-
-    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);
-    }
-
-    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);
-    }
-
-    if (node_is_type((AstNode*) node)) {
-        Type* type = type_build_from_ast(context.ast_alloc, (AstType *) node);
-        return type->id;
-    }
-
-    return 0;
-}
-
-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->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;
-    }
-
-    if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_Array) {
-        if (!types_are_compatible(to->Slice.ptr_to_data->Pointer.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_VarArgs) {
-        if (!types_are_compatible(to->Slice.ptr_to_data->Pointer.elem, from->VarArgs.ptr_to_data->Pointer.elem)) {
-            *err_msg = "Variadic argument to slice cast is not valid here because the types are different.";
-            return 0;
-        } else {
-            return 1;
-        }
-    }
-
-    if (from->kind == Type_Kind_Slice || to->kind == Type_Kind_Slice) {
-        *err_msg = "Cannot cast to or from a slice.";
-        return 0;
-    }
-
-    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 bh_aprintf(global_scratch_allocator, "%b", func->name->text, func->name->length);
-    }
-
-    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;
-}
-
-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;
-    }
-}
-
-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->named_values = src->named_values;
-    
-    arguments_ensure_length(dest, 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->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->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--;
-    }
-}
-
-// 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);
-
-    return value != 0;
-}
diff --git a/src/onyxbuiltins.c b/src/onyxbuiltins.c
deleted file mode 100644 (file)
index 8731a61..0000000
+++ /dev/null
@@ -1,435 +0,0 @@
-#include "onyxastnodes.h"
-#include "onyxtypes.h"
-#include "onyxerrors.h"
-#include "onyxutils.h"
-
-AstBasicType basic_type_void      = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_Void]  };
-AstBasicType basic_type_bool      = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_Bool]  };
-AstBasicType basic_type_i8        = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_I8]    };
-AstBasicType basic_type_u8        = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_U8]    };
-AstBasicType basic_type_i16       = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_I16]   };
-AstBasicType basic_type_u16       = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_U16]   };
-AstBasicType basic_type_i32       = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_I32]   };
-AstBasicType basic_type_u32       = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_U32]   };
-AstBasicType basic_type_i64       = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_I64]   };
-AstBasicType basic_type_u64       = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_U64]   };
-AstBasicType basic_type_f32       = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_F32]   };
-AstBasicType basic_type_f64       = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_F64]   };
-AstBasicType basic_type_rawptr    = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_Rawptr] };
-AstBasicType basic_type_type_expr = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, 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, 0,   NULL, NULL, &basic_types[Basic_Kind_Int_Unsized] };
-AstBasicType basic_type_float_unsized = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_Float_Unsized] };
-
-static OnyxToken simd_token = { Token_Type_Symbol, 0, "", { 0 } };
-AstBasicType basic_type_i8x16 = { Ast_Kind_Basic_Type, 0, &simd_token, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_I8X16] };
-AstBasicType basic_type_i16x8 = { Ast_Kind_Basic_Type, 0, &simd_token, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_I16X8] };
-AstBasicType basic_type_i32x4 = { Ast_Kind_Basic_Type, 0, &simd_token, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_I32X4] };
-AstBasicType basic_type_i64x2 = { Ast_Kind_Basic_Type, 0, &simd_token, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_I64X2] };
-AstBasicType basic_type_f32x4 = { Ast_Kind_Basic_Type, 0, &simd_token, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_F32X4] };
-AstBasicType basic_type_f64x2 = { Ast_Kind_Basic_Type, 0, &simd_token, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_F64X2] };
-AstBasicType basic_type_v128  = { Ast_Kind_Basic_Type, 0, &simd_token, NULL, 0,  NULL, 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, 0, NULL, 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 } };
-AstNumLit builtin_heap_start  = { Ast_Kind_NumLit, Ast_Flag_Const, &builtin_heap_start_token, NULL, NULL, (AstType *) &basic_type_rawptr, NULL, 0 };
-AstGlobal builtin_stack_top   = { Ast_Kind_Global, Ast_Flag_Const | Ast_Flag_Global_Stack_Top,  &builtin_stack_top_token, NULL, NULL, (AstType *) &basic_type_rawptr, NULL };
-
-AstType  *builtin_string_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;
-
-AstTyped *type_table_node = 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 },
-
-    { NULL, NULL, NULL },
-};
-
-bh_table(OnyxIntrinsic) 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 },
-    { "__zero_value", ONYX_INTRINSIC_ZERO_VALUE },
-
-    { "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 },
-
-    { 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);
-            assert(p);
-
-            symbol_builtin_introduce(p->scope, bsym->sym, bsym->node);
-        }
-        bsym++;
-    }
-
-    Package* p = package_lookup_or_create("builtin", context.global_scope, a);
-
-    builtin_string_type = (AstType *) symbol_raw_resolve(p->scope, "str");
-    if (builtin_string_type == NULL) {
-        onyx_report_error((OnyxFilePos) { 0 }, "'str' struct 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 }, "'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 }, "'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 }, "'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 }, "'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 }, "'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 }, "'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 }, "'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 }, "'Code' struct not found in builtin package.");
-        return;
-    }
-
-    p = package_lookup("builtin.type_info");
-    if (p != NULL) {
-        type_table_node = (AstTyped *) symbol_raw_resolve(p->scope, "type_table");
-    }
-
-    fori (i, 0, Binary_Op_Count) {
-        bh_arr_new(global_heap_allocator, operator_overloads[i], 4); 
-    }
-
-    bh_table_init(global_heap_allocator, intrinsic_table, 128);
-    IntrinsicMap* intrinsic = &builtin_intrinsics[0];
-    while (intrinsic->name != NULL) {
-        bh_table_put(OnyxIntrinsic, intrinsic_table, intrinsic->name, intrinsic->intrinsic);
-        intrinsic++;
-    }
-}
-
-void introduce_build_options(bh_allocator a) {
-    Package* p = package_lookup_or_create("runtime", context.global_scope, a);
-
-    AstNumLit* runtime_type = make_int_literal(a, context.options->runtime);
-    symbol_builtin_introduce(p->scope, "Runtime", (AstNode *) runtime_type);
-}
diff --git a/src/onyxchecker.c b/src/onyxchecker.c
deleted file mode 100644 (file)
index e2edab7..0000000
+++ /dev/null
@@ -1,2370 +0,0 @@
-#define BH_DEBUG
-#include "onyxparser.h"
-#include "onyxutils.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, msg); \
-        return Check_Error; \
-    } else { \
-        return Check_Yield_Macro; \
-    } \
-    } while (0)
-
-#define YIELD_(loc, msg, ...) do { \
-    if (context.cycle_detected) { \
-        onyx_report_error(loc, msg, __VA_ARGS__); \
-        return Check_Error; \
-    } else { \
-        return Check_Yield_Macro; \
-    } \
-    } while (0)
-
-#define ERROR(loc, msg) do { \
-    onyx_report_error(loc, msg); \
-    return Check_Error; \
-    } while (0)
-
-#define ERROR_(loc, msg, ...) do { \
-    onyx_report_error(loc, msg, __VA_ARGS__); \
-    return Check_Error; \
-    } while (0)
-
-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_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* aof);
-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_function_header(AstFunction* func);
-CheckStatus check_memres_type(AstMemRes* memres);
-CheckStatus check_memres(AstMemRes* memres);
-CheckStatus check_type(AstType* type);
-CheckStatus check_insert_directive(AstDirectiveInsert** pinsert);
-CheckStatus check_do_block(AstDoBlock** pdoblock);
-
-// HACK HACK HACK
-b32 expression_types_must_be_known = 0;
-
-#define STATEMENT_LEVEL 1
-#define EXPRESSION_LEVEL 2
-u32 current_checking_level=0;
-
-static inline void fill_in_type(AstTyped* node);
-
-static inline void fill_in_array_count(AstType* type_node) {
-    if (type_node == NULL) return;
-
-    if (type_node->kind == Ast_Kind_Type_Alias) {
-        fill_in_array_count(((AstTypeAlias *) type_node)->to);
-    }
-
-    if (type_node->kind == Ast_Kind_Array_Type) {
-        if (((AstArrayType *) type_node)->count_expr) {
-            // CLEANUP: The return value is not checked on this call.
-            check_expression(&((AstArrayType *) type_node)->count_expr);
-
-            resolve_expression_type(((AstArrayType *) type_node)->count_expr);
-        }
-    }
-}
-
-static inline void fill_in_poly_call_args(AstType* type_node) {
-    if (type_node == NULL) return;
-    if (type_node->kind != Ast_Kind_Poly_Call_Type) return;
-
-    AstPolyCallType* pctype = (AstPolyCallType *) type_node;
-
-    bh_arr_each(AstNode *, param, pctype->params) {
-        if (!node_is_type(*param)) {
-            // CLEANUP: The return value is not checked on this call.
-            check_expression((AstTyped **) param);
-            
-            resolve_expression_type((AstTyped *) *param);
-            fill_in_type((AstTyped *) *param);
-        }
-    }
-}
-
-static inline void fill_in_type(AstTyped* node) {
-    fill_in_array_count(node->type_node);
-    fill_in_poly_call_args(node->type_node);
-
-    if (node->type == NULL)
-        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(retnode->token->pos, "Trying to determine automatic return type.");
-
-            *expected_return_type = retnode->expr->type;
-            return Check_Success;
-        }
-
-        if (!type_check_or_auto_cast(&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));
-        }
-
-    } 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);
-
-        } else {
-            if (ifnode->false_stmt != NULL) CHECK(statement, (AstNode **) &ifnode->false_stmt);
-        }
-
-    } 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) CHECK(statement, (AstNode **) &whilenode->false_stmt);
-
-    return Check_Success;
-}
-
-CheckStatus check_for(AstFor* fornode) {
-    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.");
-
-    fornode->loop_type = For_Loop_Invalid;
-    if (types_are_compatible(iter_type, &basic_types[Basic_Kind_I32])) {
-        if (fornode->by_pointer) {
-            ERROR(fornode->var->token->pos, "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(fornode->var->token->pos, "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 = iter_type->Slice.ptr_to_data;
-        else                     fornode->var->type = iter_type->Slice.ptr_to_data->Pointer.elem;
-
-        fornode->loop_type = For_Loop_Slice;
-
-    }
-    else if (iter_type->kind == Type_Kind_VarArgs) {
-        if (fornode->by_pointer) {
-            ERROR_(fornode->var->token->pos, "Cannot iterate by pointer over '%s'.", type_get_name(iter_type));
-        }
-
-        fornode->var->type = iter_type->VarArgs.ptr_to_data->Pointer.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 = iter_type->DynArray.ptr_to_data;
-        else                     fornode->var->type = iter_type->DynArray.ptr_to_data->Pointer.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(fornode->var->token->pos, "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_(fornode->iter->token->pos,
-                "Cannot iterate over a '%s'.",
-                type_get_name(iter_type));
-    }
-
-    CHECK(block, fornode->stmt);
-
-    return Check_Success;
-}
-
-static b32 add_case_to_switch_statement(AstSwitch* switchnode, u64 case_value, AstBlock* block, OnyxFilePos pos) {
-    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, "Multiple cases for values '%d'.", case_value);
-        return 1;
-    }
-
-    bh_imap_put(&switchnode->case_map, case_value, (u64) block);
-    return 0;
-}
-
-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 (!type_is_integer(switchnode->expr->type) && switchnode->expr->type->kind != Type_Kind_Enum) {
-        ERROR(switchnode->expr->token->pos, "expected integer or enum type for switch expression");
-    }
-
-    // LEAK if this has to be yielded
-    bh_imap_init(&switchnode->case_map, global_heap_allocator, bh_arr_length(switchnode->cases) * 2);
-
-    switchnode->min_case = 0xffffffffffffffff;
-
-    // Umm, this doesn't check the type of the case expression to the type of the expression
-    bh_arr_each(AstSwitchCase, sc, switchnode->cases) {
-        CHECK(block, sc->block);
-
-        bh_arr_each(AstTyped *, value, sc->values) {
-            CHECK(expression, value);
-
-            if ((*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;
-            }
-
-            if (!type_check_or_auto_cast(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));
-            }
-
-            if (node_is_type((AstNode*) (*value))) {
-                Type* type = type_build_from_ast(context.ast_alloc, (AstType*) (*value));
-
-                if (add_case_to_switch_statement(switchnode, type->id, sc->block, sc->block->token->pos))
-                    return Check_Error;
-
-                continue;
-            }
-
-            if ((*value)->kind == Ast_Kind_Enum_Value) {
-                (*value) = (AstTyped *) ((AstEnumValue *) (*value))->value;
-            }
-
-            if ((*value)->kind != Ast_Kind_NumLit) {
-                ERROR((*value)->token->pos, "case statement expected compile time known integer");
-            }
-
-            resolve_expression_type((*value));
-            // promote_numlit_to_larger((AstNumLit *) (*value));
-
-            if (add_case_to_switch_statement(switchnode, ((AstNumLit *) (*value))->value.l, sc->block, sc->block->token->pos))
-                return Check_Error;
-        }
-    }
-
-    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 i32 non_baked_argument_count(Arguments* args) {
-    i32 count = 0;
-
-    bh_arr_each(AstTyped *, actual, args->values) {
-        assert((*actual)->kind == Ast_Kind_Argument);
-        if (!((AstArgument *) (*actual))->is_baked) count++;
-    }
-
-    bh_arr_each(AstNamedValue *, named_value, args->named_values) {
-        assert((*named_value)->value->kind == Ast_Kind_Argument);
-        if (!((AstArgument *) (*named_value)->value)->is_baked) count++;
-    }
-
-    return count;
-}
-
-static CheckStatus check_resolve_callee(AstCall* call, AstTyped** effective_callee) {
-    if (call->kind == Ast_Kind_Intrinsic_Call) return Check_Success;
-
-    call->callee = (AstTyped *) strip_aliases((AstNode *) call->callee);
-
-    AstTyped* callee = call->callee;
-    b32 calling_a_macro = 0;
-
-    if (callee->kind == Ast_Kind_Overloaded_Function) {
-        b32 should_yield = 0;
-        AstTyped* new_callee = find_matching_overload_by_arguments(
-            ((AstOverloadedFunction *) callee)->overloads,
-            &call->args,
-            &should_yield);
-
-        if (new_callee == NULL) {
-            if (callee->entity->state > Entity_State_Check_Types && !should_yield) {
-                report_unable_to_match_overload(call);
-                return Check_Error;
-
-            } else {
-                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);
-        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 if (callee->kind == Ast_Kind_Polymorphic_Proc) {
-        AstTyped* new_callee = (AstTyped *) polymorphic_proc_lookup((AstPolyProc *) 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;
-    }
-
-    if (!calling_a_macro) call->callee = 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 (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;
-}
-
-typedef enum ArgState {
-    AS_Expecting_Exact,
-    AS_Expecting_Typed_VA,
-    AS_Expecting_Untyped_VA,
-} ArgState;
-
-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->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;
-
-    // SPEED CLEANUP: Keeping an original copy for basically no reason except that sometimes you
-    // need to know the baked argument values in code generation.
-    // This should only be done once, but right now it is being done everytime this is checked,
-    // which can be multiple if we have to yield on a callee's type.
-    arguments_clone(&call->original_args, &call->args);
-
-    AstFunction* callee=NULL;
-    CHECK(resolve_callee, call, (AstTyped **) &callee);
-
-    // CLEANUP maybe make function_get_expected_arguments?
-    i32 non_vararg_param_count = (i32) callee->type->Function.param_count;
-    if (non_vararg_param_count > 0 &&
-        callee->type->Function.params[callee->type->Function.param_count - 1] == builtin_vararg_type_type)
-        non_vararg_param_count--;
-
-    i32 arg_count = bh_max(non_vararg_param_count, non_baked_argument_count(&call->args));
-    arguments_ensure_length(&call->args, arg_count);
-
-    char* err_msg = NULL;
-    fill_in_arguments(&call->args, (AstNode *) callee, &err_msg);
-    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->addr  = 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->flags & Ast_Flag_Intrinsic) {
-        call->kind = Ast_Kind_Intrinsic_Call;
-        call->callee = NULL;
-
-        token_toggle_end(callee->intrinsic_name);
-        char* intr_name = callee->intrinsic_name->text;
-
-        if (bh_table_has(OnyxIntrinsic, intrinsic_table, intr_name)) {
-            call->intrinsic = bh_table_get(OnyxIntrinsic, intrinsic_table, intr_name);
-
-        } else {
-            onyx_report_error(callee->token->pos, "Intrinsic not supported, '%s'.", intr_name);
-            token_toggle_end(callee->intrinsic_name);
-            return Check_Error;
-        }
-
-        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.");
-    }
-
-    Type **formal_params = callee->type->Function.params;
-
-    Type* variadic_type = NULL;
-    AstParam* variadic_param = NULL;
-
-    // SPEED CLEANUP: Caching the any type here.
-    Type* any_type = type_build_from_ast(context.ast_alloc, builtin_any_type);
-
-    ArgState arg_state = AS_Expecting_Exact;
-    u32 arg_pos = 0;
-    while (1) {
-        switch (arg_state) {
-            case AS_Expecting_Exact: {
-                if (arg_pos >= callee->type->Function.param_count) goto type_checking_done;
-
-                if (formal_params[arg_pos]->kind == Type_Kind_VarArgs) {
-                    variadic_type = formal_params[arg_pos]->VarArgs.ptr_to_data->Pointer.elem;
-                    variadic_param = &callee->params[arg_pos];
-                    arg_state = AS_Expecting_Typed_VA;
-                    continue;
-                }
-
-                if ((i16) arg_pos == callee->type->Function.vararg_arg_pos) {
-                    arg_state = AS_Expecting_Untyped_VA;
-                    continue;
-                }
-
-                if (arg_pos >= (u32) bh_arr_length(arg_arr)) goto type_checking_done;
-                if (!type_check_or_auto_cast(&arg_arr[arg_pos]->value, formal_params[arg_pos])) {
-                    ERROR_(arg_arr[arg_pos]->token->pos,
-                            "The procedure '%s' expects a value of type '%s' for %d%s parameter, got '%s'.",
-                            get_function_name(callee),
-                            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));
-                }
-
-                arg_arr[arg_pos]->va_kind = VA_Kind_Not_VA;
-                break;
-            }
-
-            case AS_Expecting_Typed_VA: {
-                if (arg_pos >= (u32) bh_arr_length(arg_arr)) goto type_checking_done;
-
-                if (variadic_type->id == any_type->id) {
-                    call->va_kind = VA_Kind_Any;
-
-                    resolve_expression_type(arg_arr[arg_pos]->value);
-                    arg_arr[arg_pos]->va_kind = VA_Kind_Any; 
-                    break;
-                }
-
-                call->va_kind = VA_Kind_Typed;
-
-                if (!type_check_or_auto_cast(&arg_arr[arg_pos]->value, variadic_type)) {
-                    onyx_report_error(arg_arr[arg_pos]->token->pos,
-                            "The procedure '%s' expects a value of type '%s' for the variadic parameter, '%b', got '%s'.",
-                            get_function_name(callee),
-                            type_get_name(variadic_type),
-                            variadic_param->local->token->text,
-                            variadic_param->local->token->length,
-                            node_get_type_name(arg_arr[arg_pos]->value));
-                    return Check_Error;
-                }
-
-                arg_arr[arg_pos]->va_kind = VA_Kind_Typed;
-                break;
-            }
-
-            case AS_Expecting_Untyped_VA: {
-                call->va_kind = VA_Kind_Untyped;
-
-                if (arg_pos >= (u32) bh_arr_length(arg_arr)) goto type_checking_done;
-
-                resolve_expression_type(arg_arr[arg_pos]->value);
-                if (arg_arr[arg_pos]->value->type == NULL) {
-                    ERROR(arg_arr[arg_pos]->token->pos, "Unable to resolve type for argument.");
-                }
-
-                arg_arr[arg_pos]->va_kind = VA_Kind_Untyped;
-                break;
-            }
-        }
-
-        arg_pos++;
-    }
-
-type_checking_done:
-
-    call->flags |= Ast_Flag_Has_Been_Checked;
-
-    if (arg_pos < callee->type->Function.needed_param_count)
-        ERROR(call->token->pos, "Too few arguments to function call.");
-
-    if (arg_pos < (u32) arg_count)
-        ERROR(call->token->pos, "Too many arguments to function call.");
-
-    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, "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) {
-    if (bh_arr_length(operator_overloads[binop->operation]) == 0) return NULL;
-
-    Arguments args = ((Arguments) { NULL, NULL });
-    bh_arr_new(global_heap_allocator, args.values, 2);
-    bh_arr_push(args.values, binop->left);
-    bh_arr_push(args.values, binop->right);
-
-    if (binop_is_assignment(binop->operation)) {
-        args.values[0] = (AstTyped *) make_address_of(context.ast_alloc, binop->left);
-
-        u32 current_checking_level_store = current_checking_level;
-        CheckStatus cs = check_address_of((AstAddressOf *) args.values[0]);
-        current_checking_level = current_checking_level_store;
-
-        if (cs == Check_Yield_Macro) return (AstCall *) &node_that_signals_a_yield;
-        if (cs == Check_Error) {
-            return NULL;
-        }
-    }
-
-    b32 should_yield = 0;
-    AstTyped* overload = find_matching_overload_by_arguments(operator_overloads[binop->operation], &args, &should_yield);
-    if (should_yield) {
-        bh_arr_free(args.values);
-        return (AstCall *) &node_that_signals_a_yield;
-    }
-
-    if (overload == NULL) {
-        bh_arr_free(args.values);
-        return NULL;
-    }
-
-    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;
-
-    bh_arr_each(AstTyped *, arg, args.values)
-        *arg = (AstTyped *) make_argument(context.ast_alloc, *arg);
-
-    implicit_call->args = 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) {
-            resolve_expression_type(binop->right);
-
-            if (binop->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 (binop->right->type->kind == Type_Kind_Compound) {
-                AstCompound* lhs = (AstCompound *) binop->left;
-                if (lhs->kind != Ast_Kind_Compound) {
-                    ERROR_(binop->token->pos,
-                            "Expected left hand side to have %d expressions.",
-                            binop->right->type->Compound.count);
-                }
-
-                i32 expr_count = binop->right->type->Compound.count;
-                if (bh_arr_length(lhs->exprs) != expr_count) {
-                    ERROR_(binop->token->pos,
-                            "Expected left hand side to have %d expressions.",
-                            binop->right->type->Compound.count);
-                }
-
-                fori (i, 0, expr_count) {
-                    lhs->exprs[i]->type = binop->right->type->Compound.types[i];
-                }
-
-                lhs->type = type_build_compound_type(context.ast_alloc, lhs);
-
-            } else {
-                binop->left->type = binop->right->type;
-            }
-        }
-
-    } else {
-        // NOTE: +=, -=, ...
-
-        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.");
-        }
-    }
-
-    if (!type_check_or_auto_cast(&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->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.");
-        else if (type_check_or_auto_cast(&binop->left, rtype));
-        else if (type_check_or_auto_cast(&binop->right, ltype));
-        else {
-            ERROR_(binop->token->pos,
-                    "Cannot compare '%s' to '%s'.",
-                    type_get_name(ltype),
-                    type_get_name(rtype));
-        }
-    }
-
-    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;
-
-    u32 current_checking_level_store = current_checking_level;
-    CHECK(expression, &binop->left);
-    CHECK(expression, &binop->right);
-    current_checking_level = current_checking_level_store;
-
-    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.");
-        }
-    }
-
-    // :UnaryFieldAccessIsGross
-    if (binop->left->kind == Ast_Kind_Unary_Field_Access || binop->right->kind == Ast_Kind_Unary_Field_Access) {
-        if      (type_check_or_auto_cast(&binop->left, binop->right->type));
-        else if (type_check_or_auto_cast(&binop->right, binop->left->type));
-        else {
-            report_bad_binaryop(binop);
-            return Check_Error;
-        }
-    }
-
-    // NOTE: Try operator overloading before checking everything else.
-    if ((binop->left->type != NULL && binop->right->type != NULL) &&
-        (binop->left->type->kind != Type_Kind_Basic || binop->right->type->kind != Type_Kind_Basic)) {
-        AstCall *implicit_call = binaryop_try_operator_overload(binop);
-
-        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);
-
-    // 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.");
-        }
-        else if (type_check_or_auto_cast(&binop->left, binop->right->type));
-        else if (type_check_or_auto_cast(&binop->right, binop->left->type));
-        else {
-            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];
-    }
-
-    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) return Check_Success;
-
-        if (!node_is_type((AstNode *) sl->stnode)) {
-            ERROR(sl->token->pos, "Type used for struct literal is not a type.");
-        }
-
-        fill_in_type((AstTyped *) sl);
-        if (sl->type == NULL)
-            YIELD(sl->token->pos, "Trying to resolve type of struct literal.");
-    }
-
-    if (!type_is_structlike_strict(sl->type)) {
-        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)) {
-            onyx_report_error(sl->token->pos, 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,
-                        "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);
-
-        // HACK HACK HACK
-        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 '%s'.", smem.name);
-        }
-
-        if (!type_check_or_auto_cast(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)
-            YIELD(al->token->pos, "Waiting for array literal type to be known.");
-
-        if (!node_is_type((AstNode *) al->atnode))
-            ERROR(al->token->pos, "Array type is not a type.");
-
-        fill_in_type((AstTyped *) al);
-        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;
-
-    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);
-
-        if (!type_check_or_auto_cast(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;
-    CHECK(expression, &range->low);
-    CHECK(expression, &range->high);
-
-    Type* expected_range_type = builtin_range_type_type;
-    StructMember smem;
-
-    type_lookup_member(expected_range_type, "low", &smem);
-    if (!type_check_or_auto_cast(&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);
-    if (!type_check_or_auto_cast(&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;
-    }
-
-    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);
-
-    if (!type_check_or_auto_cast(&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) {
-        // ERROR(doblock->token->pos, "Unable to determine type of do-block expression.");
-        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* aof) {
-    CHECK(expression, &aof->expr);
-    if (aof->expr->type == NULL) {
-        YIELD(aof->token->pos, "Trying to resolve type of expression to take a reference.");
-    }
-
-    if ((aof->expr->kind != Ast_Kind_Subscript
-            && aof->expr->kind != Ast_Kind_Dereference
-            && aof->expr->kind != Ast_Kind_Field_Access
-            && aof->expr->kind != Ast_Kind_Memres
-            && aof->expr->kind != Ast_Kind_Local)
-            || (aof->expr->flags & Ast_Flag_Cannot_Take_Addr) != 0) {
-        ERROR(aof->token->pos, "Cannot take the address of something that is not an l-value.");
-    }
-
-    aof->expr->flags |= Ast_Flag_Address_Taken;
-
-    aof->type = type_make_pointer(context.ast_alloc, aof->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);
-
-    // NOTE: Try operator overloading before checking everything else.
-    if ((sub->addr->type != NULL && 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);
-
-        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 (types_are_compatible(sub->expr->type, builtin_range_type_type)) {
-        Type *of = NULL;
-        if (sub->addr->type->kind == Type_Kind_Pointer)
-            of = sub->addr->type->Pointer.elem;
-        else if (sub->addr->type->kind == Type_Kind_Array)
-            of = sub->addr->type->Array.elem;
-        else {
-            // 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 (sub->expr->type->kind != Type_Kind_Basic
-            || (sub->expr->type->Basic.kind != Basic_Kind_I32 && sub->expr->type->Basic.kind != Basic_Kind_U32)) {
-        report_bad_binaryop((AstBinaryOp *) sub);
-        ERROR_(sub->token->pos,
-            "Expected type u32 or i32 for index, got '%s'.",
-            node_get_type_name(sub->expr));
-    }
-
-    if (sub->addr->type->kind == Type_Kind_Pointer)
-        sub->type = sub->addr->type->Pointer.elem;
-    else if (sub->addr->type->kind == Type_Kind_Array)
-        sub->type = sub->addr->type->Array.elem;
-    else 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;
-        sub->type = sub->addr->type->Pointer.elem;
-    }
-    else {
-        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;
-    CHECK(expression, &field->expr);
-    if (field->expr->type == NULL) {
-        // onyx_report_error(field->token->pos, "Unable to deduce type of expression for accessing field.");
-        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;
-    }
-
-    StructMember smem;
-    if (field->token != NULL && field->field == NULL) {
-        token_toggle_end(field->token);
-        // CLEANUP: Duplicating the string here isn't the best for effiency,
-        // but it fixes a lot of bugs, so here we are.
-        //                                      - brendanfh  2020/12/08
-        field->field = bh_strdup(context.ast_alloc, field->token->text);
-        token_toggle_end(field->token);
-    }
-
-    if (!type_lookup_member(field->expr->type, field->field, &smem)) {
-        AstType* type_node = field->expr->type->ast_type;
-        AstNode* n = try_symbol_raw_resolve_from_node((AstNode *) type_node, field->field);
-        if (n) {
-            *pfield = (AstFieldAccess *) n;
-            return Check_Success;
-        }
-
-        ERROR_(field->token->pos,
-            "Field '%s' does not exists on '%s'.",
-            field->field,
-            node_get_type_name(field->expr));
-    }
-
-    field->offset = smem.offset;
-    field->idx = smem.idx;
-    field->type = smem.type;
-
-    return Check_Success;
-}
-
-CheckStatus check_method_call(AstBinaryOp** mcall) {
-    CHECK(expression, &(*mcall)->left);
-    if ((*mcall)->left->type == NULL) {
-        YIELD((*mcall)->token->pos, "Trying to resolve type of left hand side.");
-    }
-
-    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)
-            implicit_argument = (AstTyped *) make_address_of(context.ast_alloc, implicit_argument);
-
-        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;
-    }
-    (*mcall)->flags |= Ast_Flag_Has_Been_Checked;
-
-    CHECK(call, &call_node);
-    call_node->next = (*mcall)->next;
-
-    *mcall = (AstBinaryOp *) call_node;
-    return Check_Success;
-}
-
-CheckStatus check_size_of(AstSizeOf* so) {
-    fill_in_array_count(so->so_ast_type);
-    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);
-
-    return Check_Success;
-}
-
-CheckStatus check_align_of(AstAlignOf* ao) {
-    fill_in_array_count(ao->ao_ast_type);
-    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);
-
-    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.
-        if (expr->kind == Ast_Kind_Typeof) {
-            CHECK(type, (AstType *) expr);
-        }
-
-        // 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.");
-            }
-        }
-
-        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;
-    }
-
-    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_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:
-            onyx_report_error(expr->token->pos,
-                    "Symbol was unresolved in symbol resolution phase, '%b'. This is definitely a compiler bug.",
-                    expr->token->text, expr->token->length);
-            retval = Check_Error;
-            break;
-
-        case Ast_Kind_Param:
-            if (expr->type == NULL) {
-                onyx_report_error(expr->token->pos, "Parameter with bad type.");
-                retval = Check_Error;
-            }
-            break;
-
-        case Ast_Kind_Local: break;
-
-        case Ast_Kind_Address_Of:    retval = check_address_of((AstAddressOf *) expr); 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, "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:
-            *pexpr = (AstTyped *) ((AstDirectiveSolidify *) expr)->resolved_proc;
-            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_StrLit: break;
-        case Ast_Kind_File_Contents: break;
-        case Ast_Kind_Overloaded_Function: break;
-        case Ast_Kind_Enum_Value: break;
-        case Ast_Kind_Memres: break;
-        case Ast_Kind_Polymorphic_Proc: break;
-        case Ast_Kind_Package: break;
-        case Ast_Kind_Error: break;
-        case Ast_Kind_Unary_Field_Access: break;
-
-        // NOTE: The only way to have an Intrinsic_Call node is to have gone through the
-        // checking of a call node at least once.
-        case Ast_Kind_Intrinsic_Call: break;
-
-        default:
-            retval = Check_Error;
-            onyx_report_error(expr->token->pos, "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);
-
-    if (!type_check_or_auto_cast(&insert->code_expr, code_type)) {
-        ERROR_(insert->token->pos, "#insert 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_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_Call: {
-            CHECK(call, (AstCall **) pstmt);
-            stmt->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.");
-            }
-            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) {
-    CHECK(statement_chain, &block->body);
-
-    // CLEANUP: There will need to be some other method of 
-    // checking the following conditions.
-    //
-    // bh_arr_each(AstTyped *, value, block->allocate_exprs) {
-    //     fill_in_type(*value);
-
-    //     if ((*value)->kind == Ast_Kind_Local) {
-    //         if ((*value)->type == NULL) {
-    //             onyx_report_error((*value)->token->pos,
-    //                     "Unable to resolve type for local '%b'.",
-    //                     (*value)->token->text, (*value)->token->length);
-    //             return Check_Error;
-    //         }
-
-    //         if ((*value)->type->kind == Type_Kind_Compound) {
-    //             onyx_report_error((*value)->token->pos,
-    //                     "Compound type not allowed as local variable type. Try splitting this into multiple variables.");
-    //             return Check_Error;
-    //         }
-    //     }
-    // }
-
-    return Check_Success;
-}
-
-CheckStatus check_function(AstFunction* func) {
-    if (func->flags & Ast_Flag_Already_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");
-    
-    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) return status;
-    }
-
-    if (*expected_return_type == &type_auto_return) {
-        *expected_return_type = &basic_types[Basic_Kind_Void];
-    }
-
-    func->flags |= Ast_Flag_Already_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, "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.");
-
-    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 stcache
-    type_build_from_ast(context.ast_alloc, (AstType *) s_node);
-    if (s_node->stcache == NULL || !s_node->stcache_is_valid)
-        YIELD(s_node->token->pos, "Waiting for type to be constructed.");
-
-    bh_arr_each(StructMember *, smem, s_node->stcache->Struct.memarr) {
-        if ((*smem)->type->kind == Type_Kind_Compound) {
-            ERROR(s_node->token->pos, "Compound types are not allowed as struct member types.");
-        }
-    }
-
-    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.");
-
-    bh_arr_each(StructMember *, smem, s_node->stcache->Struct.memarr) {
-        if ((*smem)->initial_value && *(*smem)->initial_value) {
-            CHECK(expression, (*smem)->initial_value);
-
-            if (!type_check_or_auto_cast((*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);
-        }
-    }
-
-    return Check_Success;
-}
-
-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;
-
-    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) CHECK(type, local->type_node);
-        if (local->type_node != NULL) {
-            if (!node_is_type((AstNode *) local->type_node)) {
-                ERROR(local->token->pos, "Parameter type is not a type.");
-            }
-        }
-
-        fill_in_type((AstTyped *) local);
-        if (local->type == NULL) {
-            // onyx_report_error(param->local->token->pos,
-            //         "Unable to resolve type for parameter, '%b'",
-            //         local->token->text,
-            //         local->token->length);
-            YIELD(local->token->pos, "Waiting for parameter type to be known.");
-        }
-
-        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 (!type_check_or_auto_cast(&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) {
-    if (memres->initial_value != NULL) {
-        CHECK(expression, &memres->initial_value);
-        resolve_expression_type(memres->initial_value);
-
-        if ((memres->initial_value->flags & Ast_Flag_Comptime) == 0) {
-            ERROR(memres->initial_value->token->pos, "Top level expressions must be compile time known.");
-        }
-
-        if (memres->type != NULL) {
-            Type* memres_type = memres->type;
-            if (!type_check_or_auto_cast(&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 {
-            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;
-        }
-    }
-
-    return Check_Success;
-}
-
-CheckStatus check_type(AstType* type) {
-    if (type == NULL) return Check_Success;
-    
-    while (type->kind == Ast_Kind_Type_Alias)
-        type = ((AstTypeAlias *) type)->to;
-
-    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);
-                }
-            }
-
-            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: CHECK(type, ((AstPointerType *) type)->elem); break;
-        case Ast_Kind_Slice_Type:   CHECK(type, ((AstSliceType *) type)->elem); break;
-        case Ast_Kind_DynArr_Type:  CHECK(type, ((AstDynArrType *) type)->elem); break;
-        case Ast_Kind_VarArg_Type:  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;
-        }    
-    }
-
-    return Check_Success;
-}
-
-CheckStatus check_static_if(AstIf* static_if) {
-    expression_types_must_be_known = 1;
-    CheckStatus result = check_expression(&static_if->cond);
-    if (result == Check_Yield_Macro) return Check_Yield_Macro;
-    expression_types_must_be_known = 0;
-
-    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) {
-        AstTyped* export = ((AstDirectiveExport *) directive)->export;
-        if (export->entity && export->entity->state <= Entity_State_Check_Types)
-            YIELD(directive->token->pos, "Waiting for export type to be known.");
-    }
-
-    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_node(AstNode* node) {
-    switch (node->kind) {
-        case Ast_Kind_Function:             return check_function((AstFunction *) node);
-        case Ast_Kind_Overloaded_Function:  return check_overloaded_function((AstOverloadedFunction *) node);
-        case Ast_Kind_Block:                return check_block((AstBlock *) node);
-        case Ast_Kind_Return:               return check_return((AstReturn *) node);
-        case Ast_Kind_If:                   return check_if((AstIfWhile *) node);
-        case Ast_Kind_Static_If:            return check_if((AstIfWhile *) node);
-        case Ast_Kind_While:                return check_while((AstIfWhile *) node);
-        case Ast_Kind_Call:                 return check_call((AstCall **) &node);
-        case Ast_Kind_Binary_Op:            return check_binaryop((AstBinaryOp **) &node);
-        default:                            return check_expression((AstTyped **) &node);
-    }
-}
-
-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_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);
-
-        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_Process_Directive:        cs = check_process_directive((AstNode *) ent->expr); break;
-
-        case Entity_Type_File_Contents: 
-            if (context.options->no_file_contents) {
-                onyx_report_error(ent->expr->token->pos, "#file_contents is disabled for this compilation.");
-            }
-            break;
-
-        default: break;
-    }
-
-    if (cs == Check_Success)          ent->state = Entity_State_Code_Gen;
-    if (cs == Check_Complete)         ent->state = Entity_State_Finalized;
-    if (cs == Check_Return_To_Symres) ent->state = Entity_State_Resolve_Symbols;
-    if (cs == Check_Yield_Macro)      ent->macro_attempts++;
-    else {
-        ent->macro_attempts = 0;
-        ent->micro_attempts = 0;
-    }
-}
diff --git a/src/onyxclone.c b/src/onyxclone.c
deleted file mode 100644 (file)
index 8940a03..0000000
+++ /dev/null
@@ -1,496 +0,0 @@
-#include "onyxastnodes.h"
-#include "onyxparser.h"
-#include "onyxutils.h"
-
-static inline b32 should_clone(AstNode* node) {
-    if (node->flags & Ast_Flag_No_Clone) 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_Enum_Type:
-        case Ast_Kind_Enum_Value:
-        case Ast_Kind_Overloaded_Function:
-        case Ast_Kind_Polymorphic_Proc:
-        case Ast_Kind_Alias:
-        case Ast_Kind_Code_Block:
-        case Ast_Kind_Macro:
-        case Ast_Kind_File_Contents:
-            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_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(AstPolyProc);
-        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_Do_Block: return sizeof(AstDoBlock);
-        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;
-}
-
-#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:
-            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);
-
-            C(AstIfWhile, cond);
-            //fallthrough
-
-        case Ast_Kind_Static_If:
-            C(AstIfWhile, true_stmt);
-            C(AstIfWhile, false_stmt);
-            break;
-
-        case Ast_Kind_Switch: {
-            AstSwitch* dw = (AstSwitch *) nn;
-            AstSwitch* sw = (AstSwitch *) node;
-
-            ((AstSwitch *) nn)->initialization = ast_clone_list(a, ((AstSwitch *) node)->initialization);
-            dw->expr = (AstTyped *) ast_clone(a, sw->expr);
-
-            dw->default_case = (AstBlock *) ast_clone(a, sw->default_case);
-
-            dw->cases = NULL;
-            bh_arr_new(global_heap_allocator, dw->cases, bh_arr_length(sw->cases));
-
-            bh_arr_each(AstSwitchCase, c, sw->cases) {
-                bh_arr(AstTyped *) new_values = NULL;
-                bh_arr_new(global_heap_allocator, new_values, bh_arr_length(c->values));
-                bh_arr_each(AstTyped *, value, c->values)
-                    bh_arr_push(new_values, (AstTyped *) ast_clone(a, *value));
-
-                AstSwitchCase sc;
-                sc.values = new_values; 
-                sc.block = (AstBlock *) ast_clone(a, c->block);
-                bh_arr_push(dw->cases, sc);
-            }
-            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->stcache = NULL;
-            break;
-        }
-
-        case Ast_Kind_Struct_Member:
-            C(AstStructMember, type_node);
-            C(AstStructMember, initial_value);
-            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: {
-            if (clone_depth > 1) {
-                clone_depth--;
-                return node;
-            }
-
-            AstFunction* df = (AstFunction *) nn;
-            AstFunction* sf = (AstFunction *) node;
-
-            if (sf->flags & Ast_Flag_Foreign) return node;
-
-            df->return_type = (AstType *) ast_clone(a, sf->return_type);
-            df->body = (AstBlock *) ast_clone(a, sf->body);
-
-            df->params = NULL;
-            bh_arr_new(global_heap_allocator, df->params, bh_arr_length(sf->params));
-
-            bh_arr_each(AstParam, param, sf->params) {
-                AstParam new_param = { 0 };
-                new_param.local = (AstLocal *) ast_clone(a, param->local);
-                new_param.default_value = (AstTyped *) ast_clone(a, param->default_value);
-                new_param.vararg_kind = param->vararg_kind;
-                bh_arr_push(df->params, new_param);
-            }
-
-            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 = (AstPolyProc *) 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_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;
-    }
-
-    clone_depth--;
-    return nn;
-}
-
-#undef C
-
-AstFunction* clone_function_header(bh_allocator a, AstFunction* func) {
-    if (func->kind != Ast_Kind_Function) return NULL;
-
-    if (func->flags & Ast_Flag_Foreign) return func;
-
-    AstFunction* new_func = onyx_ast_node_new(a, sizeof(AstFunction), func->kind);
-    memmove(new_func, func, sizeof(AstFunction));
-
-    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;
-        new_param.local = (AstLocal *) ast_clone(a, param->local);
-        new_param.default_value = (AstTyped *) ast_clone(a, param->default_value);
-        new_param.vararg_kind = param->vararg_kind;
-        bh_arr_push(new_func->params, new_param);
-    }
-
-    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_Function) return;
-
-    dest->body = (AstBlock *) ast_clone(a, source->body);
-}
diff --git a/src/onyxdoc.c b/src/onyxdoc.c
deleted file mode 100644 (file)
index ab1bc9d..0000000
+++ /dev/null
@@ -1,286 +0,0 @@
-#include "onyxdoc.h"
-#include "onyxutils.h"
-#include "onyxtypes.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);
-
-    bh_table_each_start(AstNode *, p->scope->symbols)
-        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);
-    bh_table_each_end;
-
-    bh_table_each_start(AstNode *, p->private_scope->symbols)
-        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);
-    bh_table_each_end;
-
-    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);
-
-    bh_table_each_start(Package *, context.packages);
-        DocPackage dp = doc_package_create(value, a);
-        bh_arr_push(doc.package_docs, dp);
-    bh_table_each_end;
-
-    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/onyxentities.c b/src/onyxentities.c
deleted file mode 100644 (file)
index 7a93862..0000000
+++ /dev/null
@@ -1,354 +0,0 @@
-#include "bh.h"
-#include "onyxastnodes.h"
-#include "onyxutils.h"
-
-static inline i32 entity_phase(Entity* e1) {
-    if (e1->state <= Entity_State_Parse) 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) {
-    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;
-
-        eh_shift_down(entities, min_index);
-    }
-}
-
-void entity_heap_init(EntityHeap* entities) {
-    bh_arena_init(&entities->entity_arena, global_heap_allocator, 32 * 1024);
-}
-
-// 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->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);
-}
-
-// 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);          \
-    }                                                           \
-
-
-    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_File: {
-            ent.state = Entity_State_Parse;
-            ent.type = Entity_Type_Load_File;
-            ent.include = (AstInclude *) node;
-            ENTITY_INSERT(ent);
-            break;
-        }
-
-        case Ast_Kind_Load_Path: {
-            ent.state = Entity_State_Parse;
-            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 ((node->flags & Ast_Flag_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: {
-            if ((node->flags & Ast_Flag_Foreign) != 0) {
-                ent.type   = Entity_Type_Foreign_Global_Header;
-                ent.global = (AstGlobal *) node;
-                ENTITY_INSERT(ent);
-
-            } else {
-                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_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);
-
-            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 = (AstPolyProc *) 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: {
-            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;
-        }
-
-        default: {
-            ent.type = Entity_Type_Expression;
-            ent.expr = (AstTyped *) node;
-            ENTITY_INSERT(ent);
-            break;
-        }
-    }
-
-    node->entity = entity;
-}
diff --git a/src/onyxerrors.c b/src/onyxerrors.c
deleted file mode 100644 (file)
index d6ad29c..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-#include "onyxerrors.h"
-#include "onyxutils.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, ' ', 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[97m");
-}
-
-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
-
-    bh_arr_each(OnyxError, err, errors.errors) {
-        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);
-        }
-    }
-}
-
-b32 onyx_has_errors() {
-    return bh_arr_length(errors.errors) > 0;
-}
-
-void onyx_clear_errors() {
-    bh_arr_set_length(errors.errors, 0);
-}
-
-void onyx_report_error(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,
-        .text = bh_strdup(errors.msg_alloc, msg),
-    };
-
-    bh_arr_push(errors.errors, err);
-}
-
-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,
-        .text = msg,
-    };
-
-    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/onyxlex.c b/src/onyxlex.c
deleted file mode 100644 (file)
index eefcdd1..0000000
+++ /dev/null
@@ -1,504 +0,0 @@
-#include "bh.h"
-#include "onyxlex.h"
-#include "onyxutils.h"
-#include "onyxerrors.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",
-    "proc",
-    "as",
-    "cast",
-    "while",
-    "for",
-    "break",
-    "continue",
-    "sizeof",
-    "alignof",
-    "typeof",
-    "defer",
-    "do",
-    "switch",
-    "case",
-    "fallthrough",
-    "macro",
-
-    "->",
-    "<-",
-    "---",
-    "|>",
-
-    ">=",
-    "<=",
-    "==",
-    "!=",
-    "+=",
-    "-=",
-    "*=",
-    "/=",
-    "%=",
-    "&=",
-    "|=",
-    "^=",
-    "&&",
-    "||",
-    "<<",
-    ">>",
-    ">>>",
-    "<<=",
-    ">>=",
-    ">>>=",
-    "..",
-    "~~",
-
-    "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;
-    }
-
-    // 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;
-    }
-
-    // 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);
-        LITERAL_TOKEN("as",          1, Token_Type_Keyword_As);
-        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);
-        break;
-    case 'm':
-        LITERAL_TOKEN("macro",       1, Token_Type_Keyword_Macro);
-        break;
-    case 'p':
-        LITERAL_TOKEN("package",     1, Token_Type_Keyword_Package);
-        LITERAL_TOKEN("proc",        1, Token_Type_Keyword_Proc);
-        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);
-        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_Right_Arrow);
-        LITERAL_TOKEN("<<=",         0, Token_Type_Shl_Equal);
-        LITERAL_TOKEN("<<",          0, Token_Type_Shift_Left);
-        LITERAL_TOKEN("<=",          0, Token_Type_Less_Equal);
-        break;
-
-    case '>':
-        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);
-        LITERAL_TOKEN(">=",          0, Token_Type_Greater_Equal);
-        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) || charset_contains("_$", *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;
-}
diff --git a/src/onyxparser.c b/src/onyxparser.c
deleted file mode 100644 (file)
index 1c60685..0000000
+++ /dev/null
@@ -1,2865 +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.
-
-// Things that need to be cleaned up in the parser:
-//  - control block local variables should be more extensible and reuse more code
-
-#include "onyxlex.h"
-#include "onyxerrors.h"
-#include "onyxparser.h"
-#include "onyxutils.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 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);
-static AstNode*       parse_statement(OnyxParser* parser);
-static AstType*       parse_type(OnyxParser* parser);
-static AstTypeOf*     parse_typeof(OnyxParser* parser);
-static AstStructType* parse_struct(OnyxParser* parser);
-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(OnyxParser* parser, AstTyped** ret);
-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 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, "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;
-}
-
-
-// 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;
-
-    // NOTE: Hex literals are unsigned.
-    if (int_node->token->length >= 2 && int_node->token->text[1] == 'x') {
-        if ((u64) value >= ((u64) 1 << 32))
-            int_node->type_node = (AstType *) &basic_type_u64;
-        else
-            int_node->type_node = (AstType *) &basic_type_u32;
-    } else {
-        int_node->type_node = (AstType *) &basic_type_int_unsized;
-    }
-
-    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;
-
-        if (next_tokens_are(parser, 2, Token_Type_Symbol, '=')) {
-            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)) break;
-            if (parse_possible_quick_function_definition(parser, &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: {
-            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->addr      = 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, "Expected '{' after 'do', got '%s'.", token_name(parser->curr->type));
-                retval = NULL;
-                break;
-            }
-
-            do_block->block = parse_block(parser, 1);
-
-            retval = (AstTyped *) do_block;
-            break;
-        }
-
-        // :TypeValueInterchange
-        case '<': {
-            AstTypeAlias* alias = make_node(AstTypeAlias, Ast_Kind_Type_Alias);
-            alias->token = expect_token(parser, '<');
-            alias->to = parse_type(parser);
-            expect_token(parser, '>');
-
-            retval = (AstTyped *) alias;
-            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_token = expect_token(parser, Token_Type_Literal_String);
-                fc->type = type_make_slice(parser->allocator, &basic_types[Basic_Kind_U8]);
-
-                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->addr  = 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, "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 = (AstPolyProc *) 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 = parse_type(parser);
-
-                    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, "code")) {
-                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);
-                    ((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 (parse_possible_directive(parser, "insert")) {
-                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;
-            }
-
-            onyx_report_error(parser->curr->pos, "Invalid directive in expression.");
-            return NULL;
-        }
-
-        default:
-        no_match:
-            onyx_report_error(parser->curr->pos, "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 '(': {
-                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_Right_Arrow: {
-                AstBinaryOp* method_call = make_node(AstBinaryOp, Ast_Kind_Method_Call);
-                method_call->token = expect_token(parser, Token_Type_Right_Arrow);
-                method_call->left = retval;
-                method_call->right = parse_factor(parser);
-
-                retval = (AstTyped *) method_call;
-                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;
-
-                // nocheckin This should maybe be goto factor_parsed; ??
-                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;
-
-        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;
-        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_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);
-
-    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);
-
-        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);
-        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;
-
-    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);
-
-    if (consume_token_if_next(parser, Token_Type_Keyword_Else)) {
-        while_node->false_stmt = parse_block(parser, 1);
-    }
-
-    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 (consume_token_if_next(parser, '^')) {
-        for_node->by_pointer = 1;
-    }
-
-    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, ':');
-    for_node->iter = parse_expression(parser, 1);
-    for_node->stmt = parse_block(parser, 1);
-
-    return for_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);
-
-    bh_arr_new(global_heap_allocator, switch_node->cases, 4);
-
-    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;
-
-    expect_token(parser, '{');
-
-    while (consume_token_if_next(parser, Token_Type_Keyword_Case)) {
-        if (parser->hit_unexpected_token) return switch_node;
-
-        bh_arr(AstTyped *) case_values = NULL;
-        bh_arr_new(global_heap_allocator, case_values, 1);
-
-        if (parse_possible_directive(parser, "default")) {
-            switch_node->default_case = parse_block(parser, 1);
-
-            if (parser->curr->type != '}') {
-                onyx_report_error(parser->curr->pos, "The #default case must be the last case in a switch statement.\n");
-            }
-            break;
-        }
-
-        AstTyped* value = parse_expression(parser, 1);
-        bh_arr_push(case_values, value);
-        while (consume_token_if_next(parser, ',')) {
-            if (parser->hit_unexpected_token) return switch_node;
-
-            value = parse_expression(parser, 1);
-            bh_arr_push(case_values, value);
-        }
-
-        AstBlock* block = parse_block(parser, 1);
-
-        AstSwitchCase sc_node;
-        sc_node.block  = block;
-        sc_node.values = case_values;
-
-        bh_arr_push(switch_node->cases, sc_node);
-    }
-
-    expect_token(parser, '}');
-    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 != ',') 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);
-        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;
-
-        AstNode* sym_node = make_symbol(parser->allocator, local_sym);
-        bh_arr_push(local_compound->exprs, (AstTyped *) sym_node);
-
-        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 == ',') {
-        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 == ':') {
-        AstBinding* binding = parse_top_level_binding(parser, symbol);
-        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.symbol_name = expect_token(parser, Token_Type_Symbol);
-
-            if (consume_token_if_next(parser, Token_Type_Keyword_As))
-                qu.as_name = expect_token(parser, Token_Type_Symbol);
-            else
-                qu.as_name = qu.symbol_name;
-
-            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);
-            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);
-            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_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")) {
-                AstLocal* context_tmp = make_local(parser->allocator, NULL, builtin_context_variable->type_node);
-
-                AstBinaryOp* assignment = make_node(AstBinaryOp, Ast_Kind_Binary_Op);
-                assignment->operation = Binary_Op_Assign;
-                assignment->left = (AstTyped *) context_tmp;
-                assignment->right = builtin_context_variable;
-                context_tmp->next = (AstNode *) assignment;
-
-                AstBinaryOp* assignment2 = make_node(AstBinaryOp, Ast_Kind_Binary_Op);
-                assignment2->operation = Binary_Op_Assign;
-                assignment2->left = builtin_context_variable;
-                assignment2->right = (AstTyped *) context_tmp;
-
-                AstBlock* context_block = parse_block(parser, 1);
-                assignment->next = (AstNode *) context_block;
-
-                AstDefer* defer_node = make_node(AstDefer, Ast_Kind_Defer);
-                defer_node->stmt = (AstNode *) assignment2;
-                defer_node->next = context_block->body;
-                context_block->body = (AstNode *) defer_node;
-
-                needs_semicolon = 0;
-                retval = (AstNode *) context_tmp;
-                break;
-            }
-
-            if (next_tokens_are(parser, 2, '#', Token_Type_Keyword_If)) {
-                AstIf* static_if = parse_static_if_stmt(parser, 1);
-                ENTITY_SUBMIT(static_if);
-
-                needs_semicolon = 0;
-                retval = (AstNode *) static_if;
-                break;
-            }
-
-            if (parse_possible_directive(parser, "persist")) {
-                // :Duplicated from parse_top_level_statement
-                AstMemRes* memres = make_node(AstMemRes, Ast_Kind_Memres);
-                memres->token = expect_token(parser, Token_Type_Symbol);
-                expect_token(parser, ':');
-
-                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);
-
-                AstBinding* binding = make_node(AstBinding, Ast_Kind_Binding);
-                binding->token = memres->token;
-                binding->node = (AstNode *) memres;
-                ENTITY_SUBMIT(binding);
-                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) {
-    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);
-        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, "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 Token_Type_Keyword_Proc: {
-                OnyxToken* proc_token = expect_token(parser, Token_Type_Keyword_Proc);
-                onyx_report_warning(proc_token->pos, "Warning: 'proc' is a deprecated keyword.");
-                *next_insertion = parse_function_type(parser, proc_token);
-                next_insertion = NULL;
-                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 == '(') {
-                    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_type(parser);
-                        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;
-            }
-
-            case '#': {
-                // :ValueDirectiveHack
-                if (parse_possible_directive(parser, "value")) {
-                // It is very weird to put these here.
-                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:
-                    *next_insertion = (AstType *) parse_expression(parser, 0);
-                    next_insertion = NULL;
-                    break;
-                }
-
-                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, "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 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;
-
-    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;
-    }
-
-    bh_arr_new(global_heap_allocator, s_node->members, 4);
-
-    while (parser->curr->type == '#') {
-        if (parser->hit_unexpected_token) return NULL;
-
-        if (parse_possible_directive(parser, "union")) {
-            s_node->flags |= Ast_Flag_Struct_Is_Union;
-        }
-
-        else if (parse_possible_directive(parser, "align")) {
-            AstNumLit* numlit = parse_int_literal(parser);
-            if (numlit == NULL) return NULL;
-
-            s_node->min_alignment = numlit->value.i;
-        }
-
-        else if (parse_possible_directive(parser, "size")) {
-            AstNumLit* numlit = parse_int_literal(parser);
-            if (numlit == NULL) return NULL;
-
-            s_node->min_size = numlit->value.i;
-        }
-
-        else {
-            OnyxToken* directive_token = expect_token(parser, '#');
-            OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
-
-            onyx_report_error(directive_token->pos, "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;
-
-        member_is_used = consume_token_if_next(parser, Token_Type_Keyword_Use);
-
-        if (next_tokens_are(parser, 3, Token_Type_Symbol, ':', ':')) {
-            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* 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, ';');
-
-        } else {
-            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, "'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, "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;
-
-                if (member_is_used) mem->flags |= Ast_Flag_Struct_Mem_Used;
-
-                bh_arr_push(s_node->members, mem);
-            }
-
-            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 {
-        return s_node;
-    }
-}
-
-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);
-
-    b32 param_use = 0;
-    b32 param_is_baked = 0;
-    OnyxToken* symbol;
-    while (!consume_token_if_next(parser, ')')) {
-        if (parser->hit_unexpected_token) return;
-
-        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);
-        expect_token(parser, ':');
-
-        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.local->flags |= Ast_Flag_Param_Use;
-            param_use = 0;
-        }
-
-        if (parser->curr->type != '=') {
-            if (consume_token_if_next(parser, Token_Type_Dot_Dot)) {
-                if (consume_token_if_next(parser, '.')) curr_param.vararg_kind = VA_Kind_Untyped;
-                else                                    curr_param.vararg_kind = VA_Kind_Typed;
-            }
-
-            if (curr_param.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);
-                curr_param.local->type_node = parse_type(parser);
-                i32 new_len = bh_arr_length(*parser->polymorph_context.poly_params);
-
-                if (curr_param.vararg_kind == VA_Kind_Typed) {
-                    AstVarArgType* va_type = make_node(AstVarArgType, Ast_Kind_VarArg_Type);
-                    va_type->elem = curr_param.local->type_node;
-                    va_type->token = curr_param.local->type_node->token;
-                    curr_param.local->type_node = (AstType *) va_type;
-                }
-
-                fori (i, 0, new_len - old_len) {
-                    (*parser->polymorph_context.poly_params)[old_len + i].type_expr = curr_param.local->type_node;
-                    (*parser->polymorph_context.poly_params)[old_len + i].idx = param_idx;
-                }
-            }
-        }
-
-        if (curr_param.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;
-                curr_param.default_value = (AstTyped *) cs;
-
-            } else {
-                curr_param.default_value = parse_expression(parser, 0);
-            }
-        }
-
-        if (param_is_baked) {
-            param_is_baked = 0;
-
-            bh_arr(AstPolyParam) pv = *parser->polymorph_context.poly_params;
-            bh_arr_push(pv, ((AstPolyParam) {
-                .kind = PPK_Baked_Value,
-                .idx = param_idx,
-
-                .poly_sym = (AstNode *) curr_param.local,
-                .type_expr = curr_param.local->type_node,
-            }));
-
-            *parser->polymorph_context.poly_params = pv;
-        }
-
-        bh_arr_push(func->params, curr_param);
-        param_idx++;
-
-        if (parser->curr->type != ')')
-            expect_token(parser, ',');
-    }
-    return;
-}
-
-static AstOverloadedFunction* parse_overloaded_function(OnyxParser* parser, OnyxToken* token) {
-    expect_token(parser, '{');
-
-    AstOverloadedFunction* ofunc = make_node(AstOverloadedFunction, Ast_Kind_Overloaded_Function);
-    ofunc->token = token;
-    ofunc->flags |= Ast_Flag_Comptime;
-
-    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_new(global_heap_allocator, func_def->params, 4);
-
-    bh_arr(AstPolyParam) polymorphic_vars = NULL;
-    bh_arr_new(global_heap_allocator, polymorphic_vars, 4);
-
-    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;
-    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);
-        }
-    }
-
-    while (parser->curr->type == '#') {
-        if (parse_possible_directive(parser, "intrinsic")) {
-            func_def->flags |= Ast_Flag_Intrinsic;
-
-            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->flags |= Ast_Flag_Foreign;
-        }
-
-        // 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, "unknown directive '#%b'.", symbol_token->text, symbol_token->length);
-        }
-    }
-
-    func_def->body = parse_block(parser, 1);
-
-    if (bh_arr_length(polymorphic_vars) > 0) {
-        AstPolyProc* pp = make_node(AstPolyProc, Ast_Kind_Polymorphic_Proc);
-        pp->token = func_def->token;
-        pp->poly_params = polymorphic_vars;
-        pp->base_func = func_def;
-
-        return (AstFunction *) pp;
-
-    } else {
-        bh_arr_free(polymorphic_vars);
-        return func_def;
-    }
-}
-
-static b32 parse_possible_function_definition(OnyxParser* parser, AstTyped** ret) {
-    if (parser->curr->type == '(') {
-        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_Type_Right_Arrow
-            && token_after_paren->type != '{'
-            && token_after_paren->type != Token_Type_Keyword_Do
-            && token_after_paren->type != Token_Type_Empty_Block)
-            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;
-
-        OnyxToken* proc_token = parser->curr;
-        AstFunction* func_node = parse_function_definition(parser, proc_token);
-        ENTITY_SUBMIT(func_node);
-        *ret = (AstTyped *) func_node;
-        return 1;
-    }
-
-    return 0;
-}
-
-static b32 parse_possible_quick_function_definition(OnyxParser* parser, AstTyped** ret) {
-    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;
-
-    OnyxToken* proc_token = expect_token(parser, '(');
-
-    bh_arr(OnyxToken*) params=NULL;
-    bh_arr_new(global_heap_allocator, params, 4);
-
-    while (parser->curr->type != ')') {
-        if (parser->hit_unexpected_token) return 0;
-
-        bh_arr_push(params, expect_token(parser, Token_Type_Symbol));
-
-        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(OnyxToken*, param, params) {
-        char text[512];
-        memset(text, 0, 512);
-        strncat(text, "__type_", 511);
-        token_toggle_end(*param);
-        strncat(text, (*param)->text, 511);
-        token_toggle_end(*param);
-
-        OnyxToken* new_token = bh_alloc(parser->allocator, sizeof(OnyxToken));
-        new_token->type = Token_Type_Symbol;
-        new_token->length = 7 + (*param)->length;
-        new_token->text = bh_strdup(parser->allocator, text);
-        new_token->pos = (*param)->pos;
-
-        AstNode* type_node = make_symbol(parser->allocator, new_token);
-        bh_arr_push(poly_params, type_node);
-    }
-
-    AstFunction* func_node = make_node(AstFunction, Ast_Kind_Function);
-    AstPolyProc* poly_proc = make_node(AstPolyProc, Ast_Kind_Polymorphic_Proc);
-
-    bh_arr_new(global_heap_allocator, func_node->params, bh_arr_length(params));
-    fori (i, 0, bh_arr_length(params)) {
-        AstLocal* param_local = make_local(parser->allocator, params[i], (AstType *) poly_params[i]);
-        param_local->kind = Ast_Kind_Param;
-
-        bh_arr_push(func_node->params, ((AstParam) {
-            .local = param_local,
-            .default_value = NULL,
-
-            .vararg_kind = 0,
-            .use_processed = 0,
-        }));
-    }
-
-    AstBlock* body_block;
-    AstType*  return_type;
-
-    if (parser->curr->type == '{') {
-        body_block = parse_block(parser, 1);
-        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;
-    }
-
-    func_node->token = proc_token;
-    func_node->body = body_block;
-    func_node->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,
-        }));
-    }
-    poly_proc->base_func = func_node;
-
-    ENTITY_SUBMIT(poly_proc);
-    *ret = (AstTyped *) poly_proc;
-
-    bh_arr_free(params);
-    bh_arr_free(poly_params);
-    return 1;
-}
-
-static AstTyped* parse_global_declaration(OnyxParser* parser) {
-    AstGlobal* global_node = make_node(AstGlobal, Ast_Kind_Global);
-    global_node->token = expect_token(parser, Token_Type_Keyword_Global);
-
-    while (parser->curr->type == '#') {
-        if (parse_possible_directive(parser, "foreign")) {
-            global_node->foreign_module = expect_token(parser, Token_Type_Literal_String);
-            global_node->foreign_name   = expect_token(parser, Token_Type_Literal_String);
-
-            global_node->flags |= Ast_Flag_Foreign;
-        }
-
-        else {
-            OnyxToken* directive_token = expect_token(parser, '#');
-            OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
-
-            onyx_report_error(directive_token->pos, "unknown directive '#%b'.", symbol_token->text, symbol_token->length);
-        }
-    }
-
-    global_node->type_node = parse_type(parser);
-
-    ENTITY_SUBMIT(global_node);
-
-    return (AstTyped *) global_node;
-}
-
-static AstEnumType* parse_enum_declaration(OnyxParser* 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 (parse_possible_directive(parser, "flags")) {
-            enum_node->flags |= Ast_Flag_Enum_Is_Flags;
-        } else {
-            OnyxToken* directive_token = expect_token(parser, '#');
-            OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
-
-            onyx_report_error(directive_token->pos, "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, ':');
-
-            // TODO: Make this work for any expression.
-            evalue->value = parse_int_literal(parser);
-        }
-
-        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) {
-    AstIf* static_if_node = make_node(AstIf, Ast_Kind_Static_If);
-    static_if_node->token = expect_token(parser, '#');
-    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);
-
-    } 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);
-
-        } 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 AstMacro* parse_macro(OnyxParser* parser) {
-    AstMacro* macro = make_node(AstMacro, Ast_Kind_Macro);
-    macro->token = expect_token(parser, Token_Type_Keyword_Macro);
-    
-    // First try quick function
-    if (!parse_possible_quick_function_definition(parser, &macro->body)) {
-        // Otherwise, do a normal function
-        macro->body  = (AstTyped *) parse_function_definition(parser, macro->token);
-    }
-
-    ENTITY_SUBMIT(macro);
-    return macro;
-}
-
-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_Enum)   return (AstTyped *) parse_enum_declaration(parser);
-    if (parser->curr->type == Token_Type_Keyword_Macro)  return (AstTyped *) parse_macro(parser);
-
-    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;
-    }
-
-    return parse_expression(parser, 1);
-}
-
-static AstBinding* parse_top_level_binding(OnyxParser* parser, OnyxToken* symbol) {
-    expect_token(parser, ':');
-
-    AstTyped* node = parse_top_level_expression(parser);
-    if (parser->hit_unexpected_token || node == NULL)
-        return NULL;
-
-    // CLEANUP
-    if (node->kind == Ast_Kind_Function) {
-        AstFunction* func = (AstFunction *) node;
-
-        if (func->intrinsic_name == NULL)
-            func->intrinsic_name = symbol;
-
-        func->name = symbol;
-
-    } else if (node->kind == Ast_Kind_Polymorphic_Proc) {
-        AstPolyProc* proc = (AstPolyProc *) node;
-
-        if (proc->base_func->intrinsic_name == NULL)
-            proc->base_func->intrinsic_name = symbol;
-
-        proc->base_func->name = symbol;
-
-    } else if (node->kind == Ast_Kind_Macro) {
-        AstMacro* macro = (AstMacro *) node;
-
-        AstFunction* func = (AstFunction *) macro->body;
-        if (func->kind == Ast_Kind_Polymorphic_Proc)
-            func = (AstFunction *) ((AstPolyProc *) func)->base_func;
-
-        func->name = symbol;
-
-    } else if (node->kind == Ast_Kind_Global) {
-        AstGlobal* global = (AstGlobal *) node;
-
-        global->name = symbol;
-
-    } else if (node->kind != Ast_Kind_Overloaded_Function
-            && node->kind != Ast_Kind_StrLit) {
-
-        if (node->kind == Ast_Kind_Struct_Type
-                || node->kind == Ast_Kind_Enum_Type
-                || node->kind == Ast_Kind_Poly_Struct_Type) {
-            ((AstStructType *)node)->name = bh_aprintf(global_heap_allocator,
-                "%b", symbol->text, symbol->length);
-        }
-
-        if (node->kind == Ast_Kind_Type_Alias)    node->token = symbol;
-        else if (node_is_type((AstNode *) node));
-        else if (node->kind == Ast_Kind_Package);
-        else if (node->kind == Ast_Kind_NumLit);
-        else {
-            AstAlias* alias = make_node(AstAlias, Ast_Kind_Alias);
-            alias->token = node->token;
-            alias->alias = node;
-            node = (AstTyped *) alias;
-        }
-
-        // HACK: This should maybe be entered elsewhere?
-        ENTITY_SUBMIT(node);
-    }
-
-    AstBinding* binding = make_node(AstBinding, Ast_Kind_Binding);
-    binding->token = symbol;
-    binding->node = (AstNode *) node;
-
-    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 (parse_possible_directive(parser, "private")) {
-        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, "private_file")) {
-        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);
-            expect_token(parser, ':');
-
-            if (parser->curr->type == ':') {
-                binding = parse_top_level_binding(parser, symbol);
-                if (binding != NULL) binding->flags |= private_kind;
-
-                goto submit_binding_to_entities;
-            }
-
-            AstMemRes* memres = make_node(AstMemRes, Ast_Kind_Memres);
-            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);
-
-            binding = make_node(AstBinding, Ast_Kind_Binding);
-            binding->token = symbol;
-            binding->flags |= private_kind;
-            binding->node = (AstNode *) memres;
-
-            goto submit_binding_to_entities;
-        }
-
-        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;
-
-                OnyxToken* str_token = expect_token(parser, Token_Type_Literal_String);
-                if (str_token != NULL) {
-                    token_toggle_end(str_token);
-                    include->name = bh_strdup(parser->allocator, str_token->text);
-                    token_toggle_end(str_token);
-                }
-
-                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;
-
-                OnyxToken* str_token = expect_token(parser, Token_Type_Literal_String);
-                if (str_token != NULL) {
-                    token_toggle_end(str_token);
-                    include->name = bh_strdup(parser->allocator, str_token->text);
-                    token_toggle_end(str_token);
-                }
-
-                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, "operator")) {
-                AstDirectiveOperator *operator = make_node(AstDirectiveOperator, Ast_Kind_Directive_Operator);
-                operator->token = dir_token;
-
-                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, "Invalid binary operator.");
-                } else {
-                    operator->operator = op;
-                }
-
-                operator->overload = parse_expression(parser, 0);
-
-                ENTITY_SUBMIT(operator);
-                return;
-            }
-            else if (parse_possible_directive(parser, "add_overload") || parse_possible_directive(parser, "add_match")) {
-                AstDirectiveAddOverload *add_overload = make_node(AstDirectiveAddOverload, Ast_Kind_Directive_Add_Overload);
-                add_overload->token = dir_token;
-                add_overload->overloaded_function = (AstNode *) parse_expression(parser, 0);
-
-                expect_token(parser, ',');
-
-                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;
-                }
-
-                add_overload->overload = parse_expression(parser, 0);
-
-                ENTITY_SUBMIT(add_overload);
-                return;
-            }
-            else if (parse_possible_directive(parser, "export")) {
-                AstDirectiveExport *export = make_node(AstDirectiveExport, Ast_Kind_Directive_Export);
-                export->token = dir_token;
-                export->export_name = expect_token(parser, Token_Type_Literal_String);
-
-                export->export = parse_expression(parser, 0);
-
-                ENTITY_SUBMIT(export);
-                return;
-            }
-            else {
-                OnyxToken* directive_token = expect_token(parser, '#');
-                OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
-
-                onyx_report_error(directive_token->pos, "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);
-    }
-
-    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);
-
-        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.scope_flags = NULL;
-
-    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.scope_flags, 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;
-
-    AstUse* implicit_use_builtin = make_node(AstUse, Ast_Kind_Use);
-    AstPackage* implicit_builtin_package = make_node(AstPackage, Ast_Kind_Package);
-    implicit_builtin_package->package_name = "builtin";
-    implicit_use_builtin->expr = (AstTyped *) implicit_builtin_package;
-    ENTITY_SUBMIT(implicit_use_builtin);
-
-    parse_top_level_statements_until(parser, Token_Type_End_Stream);
-
-    parser->current_scope = parser->current_scope->parent;
-}
diff --git a/src/onyxsymres.c b/src/onyxsymres.c
deleted file mode 100644 (file)
index b048e6c..0000000
+++ /dev/null
@@ -1,1252 +0,0 @@
-#define BH_DEBUG
-#include "onyxparser.h"
-#include "onyxutils.h"
-#include "onyxastnodes.h"
-#include "onyxerrors.h"
-
-// Variables used during the symbol resolution phase.
-static Scope*       curr_scope    = NULL;
-static b32 report_unresolved_symbols = 1;
-
-// 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)
-
-typedef enum SymresStatus {
-    Symres_Success,
-    Symres_Complete,
-
-    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* call);
-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_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 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) {
-            onyx_report_error(token->pos,
-                "Unable to resolve symbol '%b'",
-                token->text,
-                token->length);
-
-            return Symres_Error;
-        } else {
-            return Symres_Yield_Macro;
-        }
-
-    } else {
-        *symbol_node = res;
-    }
-
-    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;
-
-    if (s_node->scope) {
-        // FIX: This is probably wrong for the long term.
-        s_node->scope->parent = curr_scope;
-
-        scope_enter(s_node->scope);
-    }
-
-    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 (!node_is_type((AstNode *) member->type_node)) {
-                onyx_report_error(member->token->pos, "Member type is not a type.");
-                goto struct_symres_done;
-            }
-
-            if (member->flags & Ast_Flag_Struct_Mem_Used) {
-                AstType *used = (AstType *) member->type_node;
-
-                while (used->kind == Ast_Kind_Type_Alias) {
-                    used = ((AstTypeAlias *) used)->to;
-                }
-
-                b32 use_works = (used->kind == Ast_Kind_Struct_Type || used->kind == Ast_Kind_Poly_Call_Type);
-
-                if (used->kind == Ast_Kind_Type_Raw_Alias) {
-                    AstTypeRawAlias* alias = (AstTypeRawAlias *) used;
-                    use_works = (alias->to->kind == Type_Kind_Struct);
-                }
-
-                if (!use_works) {
-                    onyx_report_error(member->token->pos,
-                            "Can only 'use' members of struct type, got '%s'.",
-                            onyx_ast_node_kind_string(used->kind));
-                    goto struct_symres_done;
-                }
-            }
-        }
-    }
-
-struct_symres_done:
-    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);
-
-            if (!node_is_type((AstNode *) *type))
-                onyx_report_error((*type)->token->pos, "Field access did not result in a type. (%s)", onyx_ast_node_kind_string((*type)->kind));
-            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;
-
-            SYMRES(type, &ftype->return_type);
-
-            if (ftype->param_count > 0) {
-                fori (i, 0, (i64) ftype->param_count) {
-                    SYMRES(type, &ftype->params[i]);
-                }
-            }
-            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;
-            pst_node->scope = scope_create(context.ast_alloc, pst_node->entity->scope, pst_node->token->pos);
-
-            bh_arr_each(AstPolyStructParam, param, pst_node->poly_params) {
-                SYMRES(type, &param->type_node);
-                param->type = type_build_from_ast(context.ast_alloc, param->type_node);
-            }
-            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(type, (AstType **) &alias->alias);
-            break;
-        }
-
-        case Ast_Kind_Typeof: {
-            AstTypeOf* type_of = (AstTypeOf *) *type;
-            SYMRES(expression, &type_of->expr);
-            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* call) {
-    SYMRES(expression, (AstTyped **) &call->callee);
-    SYMRES(arguments, &call->args);
-
-    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);
-
-    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) {
-            onyx_report_error((*fa)->token->pos, "'%b' was not found in package '%s'. Perhaps it is defined in a file that wasn't loaded?",
-                (*fa)->token->text,
-                (*fa)->token->length,
-                ((AstPackage *) (*fa)->expr)->package->name);
-            return Symres_Error;
-        } else {
-            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, "Pipe operator expected call on right side.");
-        return Symres_Error;
-    }
-
-    if ((*pipe)->left == NULL) return Symres_Error;
-
-    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) {
-    AstCall* call_node = (AstCall *) (*mcall)->right;
-    if (call_node->kind != Ast_Kind_Call) {
-        onyx_report_error((*mcall)->token->pos, "'->' expected procedure call on right side.");
-        return Symres_Error;
-    }
-
-    SYMRES(expression, &(*mcall)->left);
-    if ((*mcall)->left == NULL) return Symres_Error;
-
-    if (((*mcall)->flags & Ast_Flag_Has_Been_Symres) == 0) {
-        AstFieldAccess* implicit_field_access = make_field_access(context.ast_alloc, (*mcall)->left, NULL);
-        implicit_field_access->token = call_node->callee->token;
-        call_node->callee = (AstTyped *) implicit_field_access;
-    }
-
-    SYMRES(expression, (AstTyped **) &call_node);
-    (*mcall)->flags |= Ast_Flag_Has_Been_Symres;
-
-    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);
-    SYMRES(type, (AstType **) &sl->stnode);
-
-    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);
-    SYMRES(type, (AstType **) &al->atnode);
-
-    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_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_Address_Of:   SYMRES(expression, &((AstAddressOf *)(*expr))->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_Alias:        SYMRES(expression, &((AstAlias *) *expr)->alias); break;
-
-        case Ast_Kind_Range_Literal:
-            SYMRES(expression, &((AstRangeLiteral *)(*expr))->low);
-            SYMRES(expression, &((AstRangeLiteral *)(*expr))->high);
-
-            SYMRES(type, &builtin_range_type);
-            (*expr)->type_node = builtin_range_type;
-
-            // NOTE: This is a weird place to put this so maybe put it somewhere else eventually
-            //                                                  - brendanfh   2020/09/04
-            builtin_range_type_type = type_build_from_ast(context.ast_alloc, builtin_range_type);
-            break;
-
-        case Ast_Kind_Function:
-        case Ast_Kind_NumLit:
-            SYMRES(type, &(*expr)->type_node);
-            break;
-
-        case Ast_Kind_StrLit:
-            SYMRES(type, &builtin_string_type);
-            (*expr)->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;
-
-        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) {
-            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_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);
-
-    bh_arr_each(AstSwitchCase, sc, switchnode->cases) {
-        bh_arr_each(AstTyped *, value, sc->values)
-            SYMRES(expression, value);
-
-        SYMRES(block, sc->block);
-    }
-
-    if (switchnode->default_case)
-        SYMRES(block, switchnode->default_case);
-
-    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);
-
-    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,
-                                "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);
-            }
-        }
-
-        package_track_use_package(package->package, use->entity);
-
-        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,
-                            "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;
-
-    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;
-
-        bh_table_each_start(StructMember, st->Struct.members);
-            AstFieldAccess* fa = make_field_access(context.ast_alloc, use->expr, value.name);
-            symbol_raw_introduce(curr_scope, value.name, use->token->pos, (AstNode *) fa);
-        bh_table_each_end;
-
-        return Symres_Success;
-    }
-
-cannot_use:
-    onyx_report_error(use->token->pos, "Cannot use this because its type is unknown.");
-    return Symres_Error;
-}
-
-static SymresStatus symres_directive_solidify(AstDirectiveSolidify** psolid) {
-    AstDirectiveSolidify* solid = *psolid;
-    if (solid->resolved_proc != NULL) {
-        *psolid = (AstDirectiveSolidify *) solid->resolved_proc;
-        return Symres_Success;
-    }
-
-    SYMRES(expression, (AstTyped **) &solid->poly_proc);
-    if (solid->poly_proc && solid->poly_proc->kind == Ast_Kind_Directive_Solidify) {
-        AstPolyProc* potentially_resolved_proc = (AstPolyProc *) ((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, "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);
-        if (onyx_has_errors()) return Symres_Error;
-
-        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;
-        }
-
-        if (onyx_has_errors()) return Symres_Error;
-    }
-
-    solid->resolved_proc = polymorphic_proc_try_solidify(solid->poly_proc, solid->known_polyvars, solid->token);
-
-    // NOTE: Not a DirectiveSolidify.
-    *psolid = (AstDirectiveSolidify *) solid->resolved_proc;
-    return Symres_Success;
-}
-
-static SymresStatus symres_directive_defined(AstDirectiveDefined** pdefined) {
-    AstDirectiveDefined* defined = *pdefined;
-
-    b32 use_package_count = (context.entities.type_count[Entity_Type_Use_Package] == 0);
-    b32 old_report_unresolved_symbols = report_unresolved_symbols;
-    report_unresolved_symbols = 0;
-
-    SymresStatus ss = symres_expression(&defined->expr);
-    if ((use_package_count || old_report_unresolved_symbols) && ss != Symres_Success) {
-        // The symbol definitely was not found and there is no chance that it could be found.
-        defined->is_defined = 0;
-
-    } else {
-        if (ss == Symres_Success) {
-            defined->is_defined = 1;
-
-        } else {
-            report_unresolved_symbols = old_report_unresolved_symbols;
-            return Symres_Yield_Macro;
-        }
-    }
-
-    report_unresolved_symbols = old_report_unresolved_symbols;
-    return Symres_Success;
-}
-
-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_Jump:        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)
-        SYMRES(statement_chain, &block->body);
-
-    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);
-
-    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;
-        }
-    }
-
-    if ((func->flags & Ast_Flag_Params_Introduced) == 0) {
-        bh_arr_each(AstParam, param, func->params) {
-            symbol_introduce(curr_scope, param->local->token, (AstNode *) param->local);
-        }
-
-        func->flags |= Ast_Flag_Params_Introduced;
-    }
-
-    bh_arr_each(AstParam, param, func->params) {
-        if (param->local->type_node != NULL) {
-            SYMRES(type, &param->local->type_node);
-        }
-    }
-
-    SYMRES(type, &func->return_type);
-    if (!node_is_type((AstNode *) func->return_type)) {
-        AstType* return_type = (AstType *) strip_aliases((AstNode *) func->return_type);
-        if (return_type->kind == Ast_Kind_Symbol) return Symres_Yield_Macro;
-
-        onyx_report_error(func->token->pos, "Return type is not a type.");
-    }
-
-    scope_leave();
-
-    return Symres_Success;
-}
-
-SymresStatus symres_function(AstFunction* func) {
-    if (func->scope == NULL)
-        func->scope = scope_create(context.ast_alloc, curr_scope, func->token->pos);
-    if (func->entity_header && func->entity_header->state < Entity_State_Check_Types) return Symres_Yield_Macro;
-
-    scope_enter(func->scope);
-
-    if ((func->flags & Ast_Flag_Has_Been_Symres) == 0) {
-        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->local->flags & Ast_Flag_Param_Use) != 0 && param->use_processed == 0) {
-                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) {
-                        // HACK HACK HACK
-                        scope_leave();
-                        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;
-                    }
-
-                    bh_table_each_start(StructMember, st->Struct.members);
-                        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);
-                    bh_table_each_end;
-
-                    param->use_processed = 1;
-
-                } else if (param->local->type != NULL) {
-                    onyx_report_error(param->local->token->pos, "Can only 'use' structures or pointers to structures.");
-
-                } else {
-                    // :ExplicitTyping
-                    onyx_report_error(param->local->token->pos, "Cannot deduce type of parameter '%b'; Try adding it explicitly.",
-                        param->local->token->text,
-                        param->local->token->length);
-                }
-            }
-        }
-
-        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,
-                    "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;
-
-    enum_node->backing_type = type_build_from_ast(context.ast_alloc, enum_node->backing);
-    enum_node->scope = scope_create(context.ast_alloc, NULL, enum_node->token->pos);
-
-    type_build_from_ast(context.ast_alloc, (AstType *) enum_node);
-
-    u64 next_assign_value = (enum_node->flags & Ast_Flag_Enum_Is_Flags) ? 1 : 0;
-    bh_arr_each(AstEnumValue *, value, enum_node->values) {
-        symbol_introduce(enum_node->scope, (*value)->token, (AstNode *) *value);
-        (*value)->type = enum_node->etcache;
-
-        if ((*value)->value != NULL) {
-            // HACK
-            resolve_expression_type((AstTyped *) (*value)->value);
-            if (type_is_small_integer((*value)->value->type)) {
-                next_assign_value = (*value)->value->value.i;
-            } else if (type_is_integer((*value)->value->type)) {
-                next_assign_value = (*value)->value->value.l;
-            } else {
-                onyx_report_error((*value)->token->pos, "expected numeric integer literal for enum initialization");
-                return Symres_Error;
-            }
-
-            (*value)->value->type = enum_node->etcache;
-
-        } else {
-            AstNumLit* num = make_int_literal(context.ast_alloc, next_assign_value);
-            num->type = enum_node->etcache;
-
-            (*value)->value = num;
-        }
-
-        (*value)->flags |= Ast_Flag_Comptime;
-
-        if (enum_node->flags & Ast_Flag_Enum_Is_Flags) {
-            next_assign_value <<= 1;
-        } else {
-            next_assign_value++;
-        }
-    }
-    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);
-
-    bh_arr_each(AstStructMember *, smem, st->members) {
-        if ((*smem)->initial_value != NULL) {
-            SYMRES(expression, &(*smem)->initial_value);
-        }
-    }
-
-    if (st->scope) scope_leave();
-    return Symres_Success;
-}
-
-static SymresStatus symres_polyproc(AstPolyProc* pp) {
-    pp->flags |= Ast_Flag_Comptime;
-    pp->poly_scope = curr_scope;
-
-    bh_arr_each(AstPolyParam, param, pp->poly_params) {
-        if (param->kind != PPK_Baked_Value) continue;
-
-        // FIX: Looking up symbols immediately in the type of the baked value does not always work
-        // because I think the following should be possible:
-        //
-        //     baked_proc :: (x: $T, $f: (T) -> T) -> T ...
-        //
-        // The type of 'f' depends on resolving the value for the polyvar 'T'.
-        SYMRES(type, &param->type_expr);
-    }
-
-    return Symres_Success;
-}
-
-static SymresStatus symres_static_if(AstIf* static_if) {
-    SYMRES(expression, &static_if->cond);
-    return Symres_Success;
-}
-
-static SymresStatus symres_process_directive(AstNode* directive) {
-    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, "#add_overload directive did not resolve to an overloaded function.");
-
-            } else {
-                AstOverloadedFunction* ofunc = (AstOverloadedFunction *) add_overload->overloaded_function;
-                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, "This cannot be used as an operator overload.");
-                return Symres_Error;
-            }
-
-            if (bh_arr_length(overload->params) != 2) {
-                onyx_report_error(operator->token->pos, "Expected 2 exactly arguments for binary operator overload.");
-                return Symres_Error;
-            }
-
-            /*if (binop_is_assignment(operator->operator)) {
-                onyx_report_error(overload->token->pos, "'%s' is not currently overloadable.", binaryop_string[operator->operator]);
-                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);
-
-            export->export->flags |= Ast_Flag_Exported;
-
-            if (export->export->kind == Ast_Kind_Function) {
-                AstFunction *func = (AstFunction *) export->export;
-                func->exported_name = export->export_name;
-
-                if ((func->flags & Ast_Flag_Exported) != 0) {
-                    if ((func->flags & Ast_Flag_Foreign) != 0) {
-                        onyx_report_error(export->token->pos, "exporting a foreign function");
-                        return Symres_Error;
-                    }
-
-                    if ((func->flags & Ast_Flag_Intrinsic) != 0) {
-                        onyx_report_error(export->token->pos, "exporting a intrinsic function");
-                        return Symres_Error;
-                    }
-
-                    // NOTE: This should never happen
-                    if (func->exported_name == NULL) {
-                        onyx_report_error(export->token->pos, "exporting function without a name");
-                        return Symres_Error;
-                    }
-                }
-            }
-
-            break;
-        }
-    }
-
-    return Symres_Success;
-}
-
-static SymresStatus symres_macro(AstMacro* macro) {
-    if (macro->body->kind == Ast_Kind_Function) {
-        SYMRES(function_header, (AstFunction *) macro->body);
-    }
-    else if (macro->body->kind == Ast_Kind_Polymorphic_Proc) {
-        SYMRES(polyproc, (AstPolyProc *) macro->body);
-    }
-
-    return Symres_Success;
-}
-
-void symres_entity(Entity* ent) {
-    Scope* old_scope = NULL;
-    if (ent->scope) {
-        old_scope = curr_scope;
-        scope_enter(ent->scope);
-    }
-
-    report_unresolved_symbols = context.cycle_detected;
-                                //(context.entities.type_count[Entity_Type_Static_If] == 0 &&
-                                // context.entities.type_count[Entity_Type_Use_Package] == 0)
-                                //|| 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_Foreign_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_Foreign_Global_Header:
-        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_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_Polymorphic_Proc:        ss = symres_polyproc(ent->poly_proc); 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;
-
-        default: break;
-    }
-
-    if (ss == Symres_Yield_Macro) ent->macro_attempts++;
-    if (ss == Symres_Yield_Micro) ent->micro_attempts++;
-    if (ss == Symres_Success) {
-        ent->macro_attempts = 0;
-        ent->micro_attempts = 0;
-        ent->state = next_state;
-    }
-
-    if (ent->scope) curr_scope = old_scope;
-}
diff --git a/src/onyxtypes.c b/src/onyxtypes.c
deleted file mode 100644 (file)
index ed4f955..0000000
+++ /dev/null
@@ -1,1268 +0,0 @@
-#define BH_DEBUG
-#include "onyxtypes.h"
-#include "onyxastnodes.h"
-#include "onyxutils.h"
-#include "onyxerrors.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,                    8,  8, "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_slice_map;
-static bh_imap type_dynarr_map;
-static bh_imap type_vararg_map;
-static bh_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_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);
-    bh_table_init(global_heap_allocator, type_func_map, 64);
-
-    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;
-
-                    if (t1_struct->Struct.memarr[0]->used)
-                        return types_are_compatible(t2_struct, t1_struct->Struct.memarr[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.ptr_to_data->Pointer.elem, t2->Slice.ptr_to_data->Pointer.elem);
-        }
-
-        case Type_Kind_VarArgs: {
-            if (t2->kind != Type_Kind_VarArgs) return 0;
-            return types_are_compatible(t1->VarArgs.ptr_to_data->Pointer.elem, t2->VarArgs.ptr_to_data->Pointer.elem);
-        }
-
-        case Type_Kind_DynArray: {
-            if (t2->kind != Type_Kind_DynArray) return 0;
-            return types_are_compatible(t1->DynArray.ptr_to_data->Pointer.elem, t2->DynArray.ptr_to_data->Pointer.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;
-        }
-
-        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 8;
-        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 16; // HACK: These should not have to be 16 bytes in size, they should only have to be 12,
-        case Type_Kind_VarArgs:  return 16; // 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 32; // data (8), count (4), capacity (4), allocator { func (4), ---(4), data (8) }
-        case Type_Kind_Compound: return type->Compound.size;
-        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 8;
-        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 8;
-        case Type_Kind_VarArgs:  return 8;
-        case Type_Kind_DynArray: return 8;
-        case Type_Kind_Compound: return 4; // HACK
-        default: return 1;
-    }
-}
-
-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: {
-            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 (bh_table_has(u64, type_func_map, name)) {
-                u64 id = bh_table_get(u64, type_func_map, name);
-                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);
-            bh_table_put(u64, type_func_map, name, func_type->id);
-
-            return func_type;
-        }
-
-        case Ast_Kind_Array_Type: {
-            AstArrayType* a_node = (AstArrayType *) type_node;
-
-            Type* a_type = type_create(Type_Kind_Array, alloc, 0);
-            a_type->Array.elem = type_build_from_ast(alloc, a_node->elem);
-
-            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((AstTyped *) 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, "Array type expects type 'i32' for size, got '%s'.",
-                        type_get_name(a_node->count_expr->type));
-                    return NULL;
-                }
-
-                count = get_expression_integer_value((AstTyped *) a_node->count_expr);
-            }
-
-            a_type->Array.count = count;
-            a_type->Array.size = type_size_of(a_type->Array.elem) * count;
-
-            type_register(a_type);
-            return a_type;
-        }
-
-        case Ast_Kind_Struct_Type: {
-            AstStructType* s_node = (AstStructType *) type_node;
-            if (s_node->stcache != NULL && s_node->stcache_is_valid) return s_node->stcache;
-
-            Type* s_type;
-            if (s_node->stcache == NULL) {
-                s_type = type_create(Type_Kind_Struct, alloc, 0);
-                s_node->stcache = 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);
-                type_register(s_type);
-
-                s_type->Struct.memarr = NULL;
-                bh_table_init(global_heap_allocator, s_type->Struct.members, s_type->Struct.mem_count + 1);
-                bh_arr_new(global_heap_allocator, s_type->Struct.memarr, s_type->Struct.mem_count);
-
-            } else {
-                s_type = s_node->stcache;
-            }
-
-            s_type->Struct.poly_sln = NULL;
-
-            bh_arr_clear(s_type->Struct.memarr);
-            bh_table_clear(s_type->Struct.members);
-
-            s_node->stcache_is_valid = 1;
-
-            b32 is_union = (s_node->flags & Ast_Flag_Struct_Is_Union) != 0;
-            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->stcache_is_valid = 0;
-                    return NULL;
-                }
-
-                mem_alignment = type_alignment_of((*member)->type);
-                if (mem_alignment <= 0) {
-                    onyx_report_error((*member)->token->pos, "Invalid member type: %s. Has alignment %d", type_get_name((*member)->type), mem_alignment); 
-                    return NULL;
-                }
-
-                if (mem_alignment > alignment) alignment = mem_alignment;
-                if (offset % mem_alignment != 0) {
-                    offset += mem_alignment - (offset % mem_alignment);
-                }
-
-                token_toggle_end((*member)->token);
-                StructMember smem = {
-                    .offset = offset,
-                    .type = (*member)->type,
-                    .idx = idx,
-                    .name = bh_strdup(alloc, (*member)->token->text),
-                    .initial_value = &(*member)->initial_value,
-                    .included_through_use = 0,
-                    .used = (((*member)->flags & Ast_Flag_Struct_Mem_Used) != 0),
-                };
-
-                if (bh_table_has(StructMember, s_type->Struct.members, (*member)->token->text)) {
-                    onyx_report_error((*member)->token->pos, "Duplicate struct member, '%s'.", (*member)->token->text);
-                    return NULL;
-                }
-                bh_table_put(StructMember, s_type->Struct.members, (*member)->token->text, smem);
-                token_toggle_end((*member)->token);
-
-                if (smem.used) {
-                    assert((*member)->type->kind == Type_Kind_Struct);
-
-                    bh_arr_each(StructMember*, psmem, (*member)->type->Struct.memarr) {
-                        StructMember new_smem = {
-                            .offset = offset + (*psmem)->offset,
-                            .type   = (*psmem)->type,
-                            .idx    = -1, // Dummy value because I don't think this is needed.
-                            .name   = (*psmem)->name,
-                            .initial_value = (*psmem)->initial_value,
-                            .included_through_use = 1,
-                            .used = 0,
-                        };
-
-                        if (bh_table_has(StructMember, s_type->Struct.members, (*psmem)->name)) {
-                            onyx_report_error((*member)->token->pos, "Duplicate struct member, '%s'.", (*psmem)->name);
-                            return NULL;
-                        }
-                        bh_table_put(StructMember, s_type->Struct.members, (*psmem)->name, new_smem);
-                    }
-                }
-
-                u32 type_size = type_size_of((*member)->type);
-
-                if (!is_union) offset += type_size;
-                if (!is_union) size = offset;
-                else           size = bh_max(size, type_size);
-
-                idx++;
-            }
-
-            // NOTE: Need to do a second pass because the references to the
-            // elements of the table may change if the internal arrays of the
-            // table need to be resized.
-            bh_arr_each(AstStructMember *, member, s_node->members) {
-                token_toggle_end((*member)->token);
-                bh_arr_push(s_type->Struct.memarr, &bh_table_get(StructMember, s_type->Struct.members, (*member)->token->text));
-                token_toggle_end((*member)->token);
-            }
-
-            alignment = bh_max(s_node->min_alignment, alignment);
-            s_type->Struct.alignment = alignment;
-
-            if (size % alignment != 0) {
-                size += alignment - (size % alignment);
-            }
-
-            size = bh_max(s_node->min_size, size);
-            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);
-
-            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->flags & Ast_Flag_Enum_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:
-            //onyx_report_error(type_node->token->pos,
-            //    "This structure is polymorphic, which means you need to provide arguments to it to make it a concrete structure. "
-            //    "This error message is probably in the wrong place, so look through your code for uses of this struct.");
-            return NULL;
-            break;
-
-        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, "Cannot instantiate a concrete type off of a non-polymorphic type.");
-                onyx_report_error(pc_type->callee->token->pos, "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,
-                    }));
-                }
-            }
-
-            AstStructType* concrete = polymorphic_struct_lookup(ps_type, slns, pc_type->token->pos);
-
-            // 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;
-            Type* struct_type = type_build_from_ast(alloc, (AstType *) concrete);
-            struct_type->Struct.constructed_from = (AstType *) ps_type;
-            return struct_type;
-        }
-
-        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;
-        }
-    }
-
-    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 (bh_table_has(u64, type_func_map, name)) {
-        u64 id = bh_table_get(u64, type_func_map, name);
-        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);
-    bh_table_put(u64, 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;
-    }
-
-    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_make_pointer(bh_allocator alloc, Type* to) {
-    if (to == NULL) return NULL;
-
-    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 = 8;
-        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;
-
-    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);
-
-    // :TypeCanBeDuplicated
-    type_register(arr_type);
-    return arr_type;
-}
-
-Type* type_make_slice(bh_allocator alloc, Type* of) {
-    if (of == NULL) return NULL;
-
-    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);
-
-        slice_type->Slice.ptr_to_data = type_make_pointer(alloc, of);
-
-        return slice_type;
-    }
-}
-
-Type* type_make_dynarray(bh_allocator alloc, Type* of) {
-    if (of == NULL) return NULL;
-
-    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);
-
-        dynarr->DynArray.ptr_to_data = type_make_pointer(alloc, of);
-
-        return dynarr;
-    }
-}
-
-Type* type_make_varargs(bh_allocator alloc, Type* of) {
-    if (of == NULL) return NULL;
-    
-    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);
-
-        va_type->VarArgs.ptr_to_data = type_make_pointer(alloc, 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;
-    }
-}
-
-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.ptr_to_data->Pointer.elem));
-        case Type_Kind_VarArgs: return bh_aprintf(global_scratch_allocator, "..%s", type_get_unique_name(type->VarArgs.ptr_to_data->Pointer.elem));
-        case Type_Kind_DynArray: return bh_aprintf(global_scratch_allocator, "[..] %s", type_get_unique_name(type->DynArray.ptr_to_data->Pointer.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);
-        }
-
-
-        default: return "unknown";
-    }
-}
-
-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.ptr_to_data->Pointer.elem));
-        case Type_Kind_VarArgs: return bh_aprintf(global_scratch_allocator, "..%s", type_get_name(type->VarArgs.ptr_to_data->Pointer.elem));
-        case Type_Kind_DynArray: return bh_aprintf(global_scratch_allocator, "[..] %s", type_get_name(type->DynArray.ptr_to_data->Pointer.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);
-        }
-
-        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;
-}
-
-static const StructMember slice_members[] = {
-    { 0, 0, NULL,                         "data",  NULL, 0, 0 },
-    { 8, 1, &basic_types[Basic_Kind_U32], "count", NULL, 0, 0 },
-};
-
-static const StructMember array_members[] = {
-    { 0,  0, NULL,                         "data",      NULL, 0, 0 },
-    { 8,  1, &basic_types[Basic_Kind_U32], "count",     NULL, 0, 0 },
-    { 12, 2, &basic_types[Basic_Kind_U32], "capacity",  NULL, 0, 0 },
-    { 16, 3, NULL,                         "allocator", NULL, 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;
-
-            if (!bh_table_has(StructMember, stype->members, member)) return 0;
-            *smem = bh_table_get(StructMember, stype->members, member);
-            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->Slice.ptr_to_data;
-
-                    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->DynArray.ptr_to_data;
-                    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->Slice.ptr_to_data;
-
-            return 1;
-        }
-
-        case Type_Kind_DynArray: {
-            if (idx > 4) return 0;
-
-            *smem = array_members[idx];
-            if (idx == 0) smem->type = type->DynArray.ptr_to_data;
-            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->Slice.ptr_to_data;
-                two->offset = 0;
-            }
-            if (idx == 1) {
-                two->type = &basic_types[Basic_Kind_U32];
-                two->offset = 8;
-            }
-
-            return 1;
-        }
-        case Type_Kind_DynArray: {
-            if (idx == 0) { 
-                two->type = type->DynArray.ptr_to_data;
-                two->offset = 0;
-            }
-            if (idx == 1) {
-                two->type = &basic_types[Basic_Kind_U32];
-                two->offset = 8;
-            }
-            if (idx == 2) {
-                two->type = &basic_types[Basic_Kind_U32];
-                two->offset = 12;
-            }
-            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 += 16;
-            }
-
-            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;
-        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 == 8) return 1;
-            return -1;
-        }
-        case Type_Kind_DynArray: {
-            if (offset == 0)   return 0;
-            if (offset == 8)   return 1;
-            if (offset == 12)  return 2;
-            if (offset == 16)  return 3;
-            if (offset == 24)  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)) {
-            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_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_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_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;
-    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;
-}
-
-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_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/onyxutils.c b/src/onyxutils.c
deleted file mode 100644 (file)
index e53256c..0000000
+++ /dev/null
@@ -1,1753 +0,0 @@
-#define BH_DEBUG
-
-#include "onyxutils.h"
-#include "onyxlex.h"
-#include "onyxastnodes.h"
-#include "onyxerrors.h"
-#include "onyxparser.h"
-#include "onyxastnodes.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) {
-    if (bh_table_has(Package *, context.packages, package_name)) {
-        return bh_table_get(Package *, context.packages, package_name);
-    } else {
-        return NULL;
-    }
-}
-
-Package* package_lookup_or_create(char* package_name, Scope* parent_scope, bh_allocator alloc) {
-    if (bh_table_has(Package *, context.packages, package_name)) {
-        return bh_table_get(Package *, context.packages, package_name);
-
-    } 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->scope = scope_create(alloc, parent_scope, (OnyxFilePos) { 0 });
-        package->private_scope = scope_create(alloc, package->scope, (OnyxFilePos) { 0 });
-        package->use_package_entities = NULL;
-
-        bh_table_put(Package *, context.packages, pac_name, package);
-
-        return package;
-    }
-}
-
-void package_track_use_package(Package* package, Entity* 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->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->symbols = NULL;
-    bh_table_init(global_heap_allocator, scope->symbols, 64);
-
-    return scope;
-}
-
-void scope_include(Scope* target, Scope* source, OnyxFilePos pos) {
-    bh_table_each_start(AstNode *, source->symbols);
-        symbol_raw_introduce(target, (char *) key, pos, value);
-    bh_table_each_end;
-}
-
-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, "_")) {
-        if (bh_table_has(AstNode *, scope->symbols, name)) {
-            if (bh_table_get(AstNode *, scope->symbols, name) != symbol) {
-                onyx_report_error(pos, "Redeclaration of symbol '%s'.", name);
-                return 0;
-            }
-            return 1;
-        }
-    }
-
-    bh_table_put(AstNode *, scope->symbols, name, symbol);
-    return 1;
-}
-
-void symbol_builtin_introduce(Scope* scope, char* sym, AstNode *node) {
-    bh_table_put(AstNode *, scope->symbols, sym, node);
-}
-
-void symbol_subpackage_introduce(Scope* scope, char* sym, AstPackage* package) {
-    if (bh_table_has(AstNode *, scope->symbols, sym)) {
-        AstNode* maybe_package = bh_table_get(AstNode *, scope->symbols, sym);
-        
-        // CLEANUP: Make this assertion an actual error message.
-        assert(maybe_package->kind == Ast_Kind_Package);
-    } else {
-        bh_table_put(AstNode *, scope->symbols, sym, (AstNode *) package);
-    }
-}
-
-AstNode* symbol_raw_resolve(Scope* start_scope, char* sym) {
-    AstNode* res = NULL;
-    Scope* scope = start_scope;
-
-    while (res == NULL && scope != NULL) {
-        if (bh_table_has(AstNode *, scope->symbols, sym)) {
-            res = bh_table_get(AstNode *, scope->symbols, sym);
-        } else {
-            scope = scope->parent;
-        }
-    }
-
-    return res;
-}
-
-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_Enum_Type: {
-            AstEnumType* etype = (AstEnumType *) node;
-            return symbol_raw_resolve(etype->scope, symbol);
-        }
-
-        case Ast_Kind_Struct_Type: {
-            AstStructType* stype = (AstStructType *) node;
-            AstNode* result = symbol_raw_resolve(stype->scope, symbol);
-
-            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);
-        }
-    }
-
-    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;
-}
-
-void scope_clear(Scope* scope) {
-    bh_table_clear(scope->symbols);
-}
-
-
-//
-// Polymorphic Procedures
-//
-
-// HACK HACK HACK nocheckin
-static b32 flag_to_yield = 0;
-
-AstTyped node_that_signals_a_yield = { Ast_Kind_Function, 0 };
-
-static void ensure_polyproc_cache_is_created(AstPolyProc* pp) {
-    if (pp->concrete_funcs == NULL) {
-        bh_table_init(global_heap_allocator, pp->concrete_funcs, 16);
-    }
-}
-
-static void insert_poly_slns_into_scope(Scope* scope, bh_arr(AstPolySolution) slns) {
-    bh_arr_each(AstPolySolution, sln, slns) {
-        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;
-                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);
-    }
-}
-
-// NOTE: This might return a volatile string. Do not store it without copying it.
-static char* build_poly_solution_key(AstPolySolution* sln) {
-    static char buffer[128];
-
-    if (sln->kind == PSK_Type) {
-        return (char *) type_get_unique_name(sln->type);
-    }
-
-    else if (sln->kind == PSK_Value) {
-        fori (i, 0, 128) 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, b32 header_already_processed) {
-    solidified_func.func->flags |= Ast_Flag_Function_Used;
-    solidified_func.func->flags |= Ast_Flag_From_Polymorphism;
-
-    EntityState header_start_state = Entity_State_Resolve_Symbols;
-    if (header_already_processed) header_start_state = Entity_State_Code_Gen;
-
-    Entity func_header_entity = {
-        .state = header_start_state,
-        .type = Entity_Type_Function_Header,
-        .function = solidified_func.func,
-        .package = NULL,
-        .scope = solidified_func.poly_scope,
-    };
-
-    entity_bring_to_state(&func_header_entity, Entity_State_Code_Gen);
-    if (onyx_has_errors()) return 0;
-
-    Entity func_entity = {
-        .state = Entity_State_Resolve_Symbols,
-        .type = Entity_Type_Function,
-        .function = solidified_func.func,
-        .package = NULL,
-        .scope = solidified_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->entity_header = entity_header;
-    solidified_func.func->entity_body   = 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(
-    AstPolyProc* pp,
-    bh_arr(AstPolySolution) slns,
-    OnyxToken* tkn,
-    b32 header_only) {
-
-    AstSolidifiedFunction solidified_func;
-    solidified_func.header_complete = 0;
-
-    // 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;
-
-    solidified_func.poly_scope = scope_create(context.ast_alloc, pp->poly_scope, poly_scope_pos);
-    insert_poly_slns_into_scope(solidified_func.poly_scope, slns);
-
-    if (header_only) {
-        solidified_func.func = (AstFunction *) clone_function_header(context.ast_alloc, pp->base_func);
-        solidified_func.func->flags |= Ast_Flag_Incomplete_Body;
-
-    } else {
-        solidified_func.func = (AstFunction *) ast_clone(context.ast_alloc, pp->base_func);
-    }
-
-    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(AstPolyProc* pp, AstSolidifiedFunction solidified_func) {
-    if (solidified_func.func->flags & Ast_Flag_Incomplete_Body) {
-        clone_function_body(context.ast_alloc, solidified_func.func, pp->base_func);
-
-        // 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, 1));
-
-        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_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) break;
-
-                bh_arr_push(elem_queue, ((PolySolveElem) {
-                    .type_expr = ((AstSliceType *) elem.type_expr)->elem,
-                    .kind = PSK_Type,
-                    .actual = elem.actual->Slice.ptr_to_data->Pointer.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.ptr_to_data->Pointer.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_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(AstPolyProc* pp, AstPolyParam* param, Arguments* args, char** err_msg) {
-    bh_arr(AstTyped *) arg_arr = args->values;
-    bh_arr(AstNamedValue *) named_values = args->named_values;
-
-    // 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 = pp->base_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;
-            }
-        }
-
-        // 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;
-}
-
-// 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, AstPolyProc* pp, 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(pp, param, args, err_msg);
-            if (typed_param == NULL) return;
-
-            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;
-    }
-
-    *resolved = solve_poly_type(param->poly_sym, param->type_expr, actual_type);
-}
-
-
-// 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.
-// 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, AstPolyProc* pp, 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(pp, 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;
-    }
-
-    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;
-        }
-
-        if (param->type == NULL)
-            param->type = type_build_from_ast(context.ast_alloc, param->type_expr);
-        assert(param->type);
-
-        if (!type_check_or_auto_cast(&value, param->type)) {
-            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(pp->base_func),
-                    type_get_name(param->type),
-                    param->idx + 1,
-                    bh_num_suffix(param->idx + 1),
-                    node_get_type_name(value));
-            return;
-        }
-
-        *resolved = ((PolySolveResult) { PSK_Value, value });
-    }
-
-    if (orig_value->kind == Ast_Kind_Argument) {
-        ((AstArgument *) orig_value)->is_baked = 1;
-    }
-}
-
-// 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(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual, char** err_msg) {
-    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);
-
-    bh_arr_each(AstPolyParam, param, pp->poly_params) {
-
-        // NOTE: First check to see if this polymorphic variable was already specified in the
-        // known solutions.
-        b32 already_solved = 0;
-        bh_arr_each(AstPolySolution, known_sln, pp->known_slns) {
-            if (token_equals(param->poly_sym->token, known_sln->poly_sym->token)) {
-                already_solved = 1;
-                break;
-            }
-        }
-        if (already_solved) continue;
-
-        // 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, pp, param, pp_lookup, actual, err_msg); break;
-            case PPK_Baked_Value: solve_for_polymorphic_param_value(&resolved, pp, param, pp_lookup, actual, err_msg); break;
-
-            default: if (err_msg) *err_msg = "Invalid polymorphic parameter kind. This is a compiler bug.";
-        }
-
-        if (flag_to_yield) goto sln_not_found;
-        
-        switch (resolved.kind) {
-            case PSK_Undefined:
-                // 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);
-
-                goto sln_not_found;
-
-            case PSK_Type:
-                bh_arr_push(slns, ((AstPolySolution) {
-                    .kind     = PSK_Type,
-                    .poly_sym = param->poly_sym,
-                    .type     = resolved.actual,
-                }));
-                break;
-
-            case PSK_Value:
-                bh_arr_push(slns, ((AstPolySolution) {
-                    .kind     = PSK_Value,
-                    .poly_sym = param->poly_sym,
-                    .value    = resolved.value,
-                }));
-                break;
-        }
-    }
-
-    return slns;
-
-    sln_not_found:
-    bh_arr_free(slns);
-    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(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual, OnyxToken* tkn) {
-    ensure_polyproc_cache_is_created(pp);
-
-    char *err_msg = NULL;
-    bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, pp_lookup, actual, &err_msg);
-    if (slns == NULL) {
-        if (flag_to_yield) {
-            flag_to_yield = 0;
-            return (AstFunction *) &node_that_signals_a_yield;
-        }
-
-        if (err_msg != NULL) onyx_report_error(tkn->pos, err_msg);
-        else                 onyx_report_error(tkn->pos, "Some kind of error occured when generating a polymorphic procedure. You hopefully will not see this");
-
-        return NULL;
-    }
-
-    AstFunction* result = polymorphic_proc_solidify(pp, slns, tkn);
-    
-    bh_arr_free(slns);
-    return result;
-}
-
-AstFunction* polymorphic_proc_solidify(AstPolyProc* 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);
-    if (bh_table_has(AstSolidifiedFunction, pp->concrete_funcs, unique_key)) {
-        AstSolidifiedFunction solidified_func = bh_table_get(AstSolidifiedFunction, pp->concrete_funcs, unique_key);
-
-        // 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);
-
-    // NOTE: Cache the function for later use, reducing duplicate functions.
-    bh_table_put(AstSolidifiedFunction, pp->concrete_funcs, unique_key, solidified_func);
-
-    if (!add_solidified_function_entities(solidified_func, 0)) {
-        onyx_report_error(tkn->pos, "Error in polymorphic procedure header generated from this call site.");
-        return NULL;
-    }
-
-    return solidified_func.func;
-}
-
-// NOTE: This can return either a AstFunction or an AstPolyProc, depending if enough parameters were
-// supplied to remove all the polymorphic variables from the function.
-AstNode* polymorphic_proc_try_solidify(AstPolyProc* 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 {
-            onyx_report_error(tkn->pos, "'%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
-        AstPolyProc* new_pp = onyx_ast_node_new(context.ast_alloc, sizeof(AstPolyProc), Ast_Kind_Polymorphic_Proc);
-        new_pp->token = pp->token;                            // TODO: Change this to be the solidify->token
-        new_pp->base_func = pp->base_func;
-        new_pp->flags = pp->flags;
-        new_pp->poly_params = 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(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual) {
-    bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, pp_lookup, actual, NULL);
-    if (slns == NULL) return NULL;
-
-    ensure_polyproc_cache_is_created(pp);
-
-    AstSolidifiedFunction solidified_func;
-
-    char* unique_key = build_poly_slns_unique_key(slns);
-    if (bh_table_has(AstSolidifiedFunction, pp->concrete_funcs, unique_key)) {
-        solidified_func = bh_table_get(AstSolidifiedFunction, pp->concrete_funcs, unique_key);
-
-    } 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.header_complete) return solidified_func.func;
-
-    Entity func_header_entity = {
-        .state = Entity_State_Resolve_Symbols,
-        .type = Entity_Type_Function_Header,
-        .function = solidified_func.func,
-        .package = NULL,
-        .scope = solidified_func.poly_scope,
-    };
-
-    b32 successful = entity_bring_to_state(&func_header_entity, Entity_State_Code_Gen);
-    if (onyx_has_errors()) {
-        onyx_errors_print();
-        onyx_clear_errors();
-        return NULL;
-    }
-
-    solidified_func.header_complete = successful;
-
-    // NOTE: Cache the function for later use, only if it didn't have errors in its header.
-    bh_table_put(AstSolidifiedFunction, pp->concrete_funcs, unique_key, solidified_func);
-    
-    return solidified_func.func;
-}
-
-//
-// 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, b32* should_yield) {
-    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 *) entry->key;
-        arguments_copy(&args, param_args);
-
-        AstFunction* overload = NULL;
-        switch (node->kind) {
-            case Ast_Kind_Function:         overload = (AstFunction *) node; break;
-            case Ast_Kind_Macro:            overload = macro_resolve_header((AstMacro *) node, param_args, NULL); break;
-            case Ast_Kind_Polymorphic_Proc: overload = polymorphic_proc_build_only_header((AstPolyProc *) node, PPLM_By_Arguments, param_args); break;
-        }
-
-        // NOTE: Overload is not something that is known to be overloadable.
-        if (overload == NULL) continue;
-        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.
-            if (should_yield) *should_yield = 1;
-
-            // 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 NULL;
-        }
-        assert(overload->type->kind == Type_Kind_Function);
-
-        // NOTE: If the arguments cannot be placed successfully in the parameters list
-        if (!fill_in_arguments(&args, (AstNode *) overload, NULL)) continue;
-
-        TypeFunction* ol_type = &overload->type->Function;
-        if (bh_arr_length(args.values) < (i32) ol_type->needed_param_count) continue;
-
-        b32 all_arguments_work = 1;
-        fori (i, 0, bh_arr_length(args.values)) {
-            if (i >= ol_type->param_count) {
-                all_arguments_work = 0;
-                break;
-            }
-
-            Type* type_to_match = ol_type->params[i];
-            AstTyped** value    = &args.values[i];
-
-            if (type_to_match->kind == Type_Kind_VarArgs) type_to_match = type_to_match->VarArgs.ptr_to_data->Pointer.elem;
-            if ((*value)->kind == Ast_Kind_Argument) {
-                // :ArgumentResolvingIsComplicated
-                if (((AstArgument *) (*value))->is_baked) continue;
-                value = &((AstArgument *) *value)->value;
-            }
-
-            if (!type_check_or_auto_cast(value, type_to_match)) {
-                all_arguments_work = 0;
-                break;
-            }
-        }
-
-        if (all_arguments_work) {
-            matched_overload = node;
-            break;
-        }
-    }
-
-    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;
-
-        if (type_check_or_auto_cast(&node, type)) {
-            matched_overload = node;
-            break;
-        }
-    }
-    
-    bh_imap_free(&all_overloads);
-    return matched_overload;
-}
-
-void report_unable_to_match_overload(AstCall* call) {
-    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, "Unable to match overloaded function with provided argument types: (%s)", arg_str);
-
-    bh_free(global_scratch_allocator, arg_str);
-}
-
-
-//
-// 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.
-    fori (i, 0, bh_arr_length(call->args.values)) {
-        symbol_introduce(argument_scope,
-            template->params[i].local->token,
-            (AstNode *) ((AstArgument *) call->args.values[i])->value);
-    }
-
-    if (template->flags & Ast_Flag_From_Polymorphism) {
-        // SLOW DUMB HACKY WAY TO DO THIS!!!!! FIX IT!!!!!
-
-        AstPolyProc* pp = (AstPolyProc *) macro->body;
-        bh_table_each_start(AstSolidifiedFunction, pp->concrete_funcs);
-
-            if (value.func == template) {
-                scope_include(argument_scope, value.poly_scope, call->token->pos);
-                break;
-            }
-
-        bh_table_each_end;
-    }
-
-    *(AstNode **) pcall = subst;
-    return;
-}
-
-AstFunction* macro_resolve_header(AstMacro* macro, Arguments* args, OnyxToken* callsite) {
-    switch (macro->body->kind) {
-        case Ast_Kind_Function: return (AstFunction *) macro->body;
-
-        case Ast_Kind_Polymorphic_Proc: {
-            AstPolyProc* pp = (AstPolyProc *) macro->body;
-            ensure_polyproc_cache_is_created(pp);
-
-            char* err_msg=NULL;
-            bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, PPLM_By_Arguments, args, &err_msg);
-
-            if (slns == NULL) {
-                if (flag_to_yield) {
-                    flag_to_yield = 0;
-                    return (AstFunction *) &node_that_signals_a_yield;
-                }
-
-                if (callsite) onyx_report_error(callsite->pos, err_msg);
-
-                return NULL;
-            }
-
-            // CLEANUP Copy'n'pasted from polymorphic_proc_build_only_header
-            AstSolidifiedFunction solidified_func;
-
-            char* unique_key = build_poly_slns_unique_key(slns);
-            if (bh_table_has(AstSolidifiedFunction, pp->concrete_funcs, unique_key)) {
-                solidified_func = bh_table_get(AstSolidifiedFunction, pp->concrete_funcs, unique_key);
-
-            } 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, callsite, 1);
-            }
-
-            if (solidified_func.header_complete) return solidified_func.func;
-
-            Entity func_header_entity = {
-                .state = Entity_State_Resolve_Symbols,
-                .type = Entity_Type_Function_Header,
-                .function = solidified_func.func,
-                .package = NULL,
-                .scope = solidified_func.poly_scope,
-            };
-
-            b32 successful = entity_bring_to_state(&func_header_entity, Entity_State_Code_Gen);
-            if (onyx_has_errors()) return NULL;
-
-            solidified_func.header_complete = successful;
-
-            bh_table_put(AstSolidifiedFunction, pp->concrete_funcs, unique_key, solidified_func);
-            if (!successful) return (AstFunction *) &node_that_signals_a_yield;
-
-            return solidified_func.func;
-        }
-
-        default: assert(("Bad macro body type.", 0));
-    }
-}
-
-//
-// 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);
-}
-
-AstStructType* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(AstPolySolution) slns, OnyxFilePos pos) {
-    // @Cleanup
-    assert(ps_type->scope != NULL);
-
-    if (ps_type->concrete_structs == NULL) {
-        bh_table_init(global_heap_allocator, ps_type->concrete_structs, 16);
-    }
-
-    if (bh_arr_length(slns) != bh_arr_length(ps_type->poly_params)) {
-        onyx_report_error(pos, "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];
-        
-        PolySolutionKind expected_kind = PSK_Undefined;
-        if ((AstNode *) ps_type->poly_params[i].type_node == (AstNode *) &basic_type_type_expr) {
-            expected_kind = PSK_Type;
-        } else {
-            expected_kind = PSK_Value;
-        }
-
-        if (sln->kind != expected_kind) {
-            if (expected_kind == PSK_Type) 
-                onyx_report_error(pos, "Expected type expression for %d%s argument.", i + 1, bh_num_suffix(i + 1));
-
-            if (expected_kind == PSK_Value)
-                onyx_report_error(pos, "Expected value expression of type '%s' for %d%s argument.",
-                    type_get_name(ps_type->poly_params[i].type),
-                    i + 1, bh_num_suffix(i + 1));
-
-            return NULL;
-        }
-
-        if (sln->kind == PSK_Value) {
-            resolve_expression_type(sln->value);
-
-            if ((sln->value->flags & Ast_Flag_Comptime) == 0) {
-                onyx_report_error(pos,
-                    "Expected compile-time known argument for '%b'.",
-                    sln->poly_sym->token->text,
-                    sln->poly_sym->token->length);
-                return NULL;
-            }
-
-            if (!types_are_compatible(sln->value->type, ps_type->poly_params[i].type)) {
-                onyx_report_error(pos, "Expected compile-time argument of type '%s', got '%s'.",
-                    type_get_name(ps_type->poly_params[i].type),
-                    type_get_name(sln->value->type));
-                return NULL;
-            }
-        }
-
-        i++;
-    }
-
-    char* unique_key = build_poly_slns_unique_key(slns);
-    if (bh_table_has(AstStructType *, ps_type->concrete_structs, unique_key)) {
-        AstStructType* concrete_struct = bh_table_get(AstStructType *, ps_type->concrete_structs, unique_key);
-
-        if (concrete_struct->entity_type->state < Entity_State_Check_Types) {
-            return NULL;
-        }
-
-        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 concrete_struct;
-    }
-
-    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);
-    bh_table_put(AstStructType *, ps_type->concrete_structs, unique_key, concrete_struct);
-
-    add_entities_for_node(NULL, (AstNode *) concrete_struct, sln_scope, NULL);
-    return NULL;
-}
-
-b32 entity_bring_to_state(Entity* ent, EntityState state) {
-    EntityState last_state = ent->state;
-
-    while (ent->state != state) {
-        switch (ent->state) {
-            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;
-
-            default: return 0;
-        }
-
-        if (ent->state == last_state) return 0;
-        last_state = ent->state;
-
-        if (onyx_has_errors()) return 0;
-    }
-
-    return 1;
-}
-
-
-//
-// 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;
-}
-
-// 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) {
-
-    { // 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)) {
-                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) {
-            *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) {
-        *err_msg = bh_aprintf(global_scratch_allocator, "Too many values provided. Expected at most %d.", maximum_arguments);
-        success = 0;
-    }
-
-    return 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;
-}
-
-char* lookup_included_file(char* filename, char* relative_to, b32 add_onyx_suffix, b32 search_included_folders) {
-    assert(relative_to != NULL);
-
-    static char path[256];
-    fori (i, 0, 256) path[i] = 0;
-
-    static char fn[128];
-    fori (i, 0, 128) fn[i] = 0;
-
-    if (!bh_str_ends_with(filename, ".onyx") && add_onyx_suffix) {
-        bh_snprintf(fn, 128, "%s.onyx", filename);
-    } else {
-        bh_snprintf(fn, 128, "%s", filename);
-    }
-
-#if defined(_BH_LINUX)
-    #define DIR_SEPARATOR '/'
-#elif defined(_BH_WINDOWS)
-    #define DIR_SEPARATOR '\\'
-#endif
-
-    fori (i, 0, 128) 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, 256, "%s%c%s", relative_to, DIR_SEPARATOR, fn + 2);
-        else
-            bh_snprintf(path, 256, "%s%s", relative_to, fn + 2);
-
-        if (bh_file_exists(path)) return bh_path_get_full_name(path, global_scratch_allocator);
-
-        return fn;
-    }
-
-    if (search_included_folders) {
-        bh_arr_each(const char *, folder, context.options->included_folders) {
-            if ((*folder)[strlen(*folder) - 1] != DIR_SEPARATOR)
-                bh_snprintf(path, 256, "%s%c%s", *folder, DIR_SEPARATOR, fn);
-            else
-                bh_snprintf(path, 256, "%s%s", *folder, fn);
-
-            if (bh_file_exists(path)) return bh_path_get_full_name(path, global_scratch_allocator);
-        }
-    }
-
-    return fn;
-
-#undef DIR_SEPARATOR
-}
-
diff --git a/src/onyxwasm.c b/src/onyxwasm.c
deleted file mode 100644 (file)
index beeddfb..0000000
+++ /dev/null
@@ -1,3672 +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 "onyxwasm.h"
-#include "onyxutils.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) {
-        return WASM_TYPE_VOID;
-    }
-
-    if (type->kind == Type_Kind_Slice) {
-        return WASM_TYPE_VOID;
-    }
-
-    if (type->kind == Type_Kind_Enum) {
-        return onyx_type_to_wasm_type(type->Enum.backing);
-    }
-
-    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_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;
-}
-
-
-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,
-};
-
-
-#define WI(instr) bh_arr_push(code, ((WasmInstruction){ instr, 0x00 }))
-#define WID(instr, data) bh_arr_push(code, ((WasmInstruction){ instr, data }))
-#define WIL(instr, data) bh_arr_push(code, ((WasmInstruction){ instr, { .l = data } }))
-#define WIP(instr, data) bh_arr_push(code, ((WasmInstruction){ instr, { .p = data } }))
-#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(type1, type2) { \
-    u64 t0 = local_raw_allocate(mod->local_alloc, type1); \
-    u64 t1 = local_raw_allocate(mod->local_alloc, type2); \
-                                                          \
-    WIL(WI_LOCAL_SET, t0);                                \
-    WIL(WI_LOCAL_SET, t1);                                \
-    WIL(WI_LOCAL_GET, t0);                                \
-    WIL(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 }))
-
-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(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(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);
-EMIT_FUNC_NO_ARGS(leave_structured_block);
-
-static void emit_raw_data(OnyxWasmModule* mod, ptr data, AstTyped* node);
-static b32 emit_raw_data_(OnyxWasmModule* mod, ptr data, AstTyped* node);
-
-#include "onyxwasm_intrinsics.c"
-#include "onyxwasm_type_table.c"
-
-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);
-    }
-
-    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);
-
-    *pcode = code;
-}
-
-EMIT_FUNC(enter_structured_block, StructuredBlockType sbt) {
-    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(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(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(WI_JUMP, labelidx);
-    } else {
-        onyx_report_error(jump->token->pos, "Invalid structured jump.");
-    }
-
-    *pcode = code;
-}
-
-EMIT_FUNC(statement, AstNode* stmt) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    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_Insert: break;
-
-        default:                  emit_expression(mod, &code, (AstTyped *) stmt); break;
-    }
-
-    *pcode = code;
-}
-
-EMIT_FUNC(local_allocation, AstTyped* stmt) {
-    bh_imap_put(&mod->local_map, (u64) stmt, local_allocate(mod->local_alloc, stmt));
-
-    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(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(WI_LOCAL_SET, localidx + i);
-
-            } else {
-                WIL(WI_LOCAL_SET, localidx);
-            }
-
-            *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(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(WI_LOCAL_SET, lptr_local);
-
-        AstArrayLiteral* al = (AstArrayLiteral *) right;
-        fori (i, 0, elem_count) {
-            WIL(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(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(WI_LOCAL_SET, expr_tmp);
-        u64 offset = 0;
-        emit_location_return_offset(mod, &code, lval, &offset);
-        WIL(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_Array) {
-        emit_array_store(mod, pcode, type, offset);
-        return;
-    }
-
-    if (type->kind == Type_Kind_Enum)     type = type->Enum.backing;
-    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(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(WI_I32_STORE_8,  ((WasmInstructionData) { alignment, offset }));
-        else if (store_size == 2)   WID(WI_I32_STORE_16, ((WasmInstructionData) { alignment, offset }));
-        else if (store_size == 4)   WID(WI_I32_STORE,    ((WasmInstructionData) { alignment, offset }));
-        else if (store_size == 8)   WID(WI_I64_STORE,    ((WasmInstructionData) { alignment, offset }));
-    } else if (is_basic && (type->Basic.flags & Basic_Flag_Float)) {
-        if      (store_size == 4)   WID(WI_F32_STORE, ((WasmInstructionData) { alignment, offset }));
-        else if (store_size == 8)   WID(WI_F64_STORE, ((WasmInstructionData) { alignment, offset }));
-    } else if (is_basic && (type->Basic.flags & Basic_Flag_SIMD)) {
-        WID(WI_V128_STORE, ((WasmInstructionData) { alignment, offset }));
-    } else {
-        onyx_report_error((OnyxFilePos) { 0 },
-            "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_Array) {
-        if (offset != 0) {
-            WID(WI_PTR_CONST, offset);
-            WI(WI_PTR_ADD);
-        }
-
-        *pcode = code;
-        return;
-    }
-
-    if (type->kind == Type_Kind_Enum)     type = type->Enum.backing;
-    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(instr, ((WasmInstructionData) { alignment, offset }));
-
-    if (instr == WI_NOP) {
-        onyx_report_error((OnyxFilePos) { 0 },
-            "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 (if_node->true_stmt) emit_block(mod, &code, if_node->true_stmt, 0);
-
-    if (if_node->false_stmt) {
-        WI(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);
-        emit_enter_structured_block(mod, &code, SBT_Continue_Loop);
-
-        emit_expression(mod, &code, while_node->cond);
-        WI(WI_I32_EQZ);
-        WID(WI_COND_JUMP, 0x01);
-
-        emit_block(mod, &code, while_node->true_stmt, 0);
-
-        if (bh_arr_last(code).type != WI_JUMP)
-            WID(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);
-        emit_enter_structured_block(mod, &code, SBT_Continue_Loop);
-
-        emit_block(mod, &code, while_node->true_stmt, 0);
-
-        emit_expression(mod, &code, while_node->cond);
-        WID(WI_COND_JUMP, 0x00);
-
-        emit_leave_structured_block(mod, &code);
-        WI(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
-
-    AstLocal* var = for_node->var;
-    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(WI_LOCAL_SET, step_local);
-    WIL(WI_LOCAL_SET, high_local);
-    WIL(WI_LOCAL_TEE, low_local);
-    WIL(WI_LOCAL_SET, iter_local);
-
-    emit_enter_structured_block(mod, &code, SBT_Breakable_Block);
-    emit_enter_structured_block(mod, &code, SBT_Basic_Loop);
-    emit_enter_structured_block(mod, &code, SBT_Continue_Block);
-
-    WIL(WI_LOCAL_GET, iter_local);
-    WIL(WI_LOCAL_GET, high_local);
-    WI(WI_I32_GE_S);
-    WID(WI_COND_JUMP, 0x02);
-
-    emit_block(mod, &code, for_node->stmt, 0);
-
-    emit_leave_structured_block(mod, &code);
-
-    WIL(WI_LOCAL_GET, iter_local);
-    WIL(WI_LOCAL_GET, step_local);
-    WI(WI_I32_ADD);
-    WIL(WI_LOCAL_SET, iter_local);
-
-    if (bh_arr_last(code).type != WI_JUMP)
-        WID(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(WI_LOCAL_TEE, ptr_local);
-    WIL(WI_PTR_CONST, for_node->iter->type->Array.count * elem_size);
-    WI(WI_PTR_ADD);
-    WIL(WI_LOCAL_SET, end_ptr_local);
-
-    emit_enter_structured_block(mod, &code, SBT_Breakable_Block);
-    emit_enter_structured_block(mod, &code, SBT_Basic_Loop);
-    emit_enter_structured_block(mod, &code, SBT_Continue_Block);
-
-    WIL(WI_LOCAL_GET, ptr_local);
-    WIL(WI_LOCAL_GET, end_ptr_local);
-    WI(WI_PTR_GE);
-    WID(WI_COND_JUMP, 0x02);
-
-    if (!for_node->by_pointer) {
-        if (!it_is_local) emit_local_location(mod, &code, var, &offset);
-
-        WIL(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(WI_LOCAL_SET, iter_local);
-    }
-
-    emit_block(mod, &code, for_node->stmt, 0);
-
-    emit_leave_structured_block(mod, &code);
-
-    WIL(WI_LOCAL_GET, ptr_local);
-    WIL(WI_PTR_CONST, elem_size);
-    WI(WI_PTR_ADD);
-    WIL(WI_LOCAL_SET, ptr_local);
-
-    if (bh_arr_last(code).type != WI_JUMP)
-        WID(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(WI_LOCAL_SET, end_ptr_local);
-    WIL(WI_LOCAL_TEE, ptr_local);
-    WIL(WI_LOCAL_GET, end_ptr_local);
-    if (elem_size != 1) {
-        WID(WI_PTR_CONST, elem_size);
-        WI(WI_PTR_MUL);
-    }
-    WI(WI_PTR_ADD);
-    WIL(WI_LOCAL_SET, end_ptr_local);
-
-    emit_enter_structured_block(mod, &code, SBT_Breakable_Block);
-    emit_enter_structured_block(mod, &code, SBT_Basic_Loop);
-    emit_enter_structured_block(mod, &code, SBT_Continue_Block);
-
-    WIL(WI_LOCAL_GET, ptr_local);
-    WIL(WI_LOCAL_GET, end_ptr_local);
-    WI(WI_PTR_GE);
-    WID(WI_COND_JUMP, 0x02);
-
-    if (!for_node->by_pointer) {
-        if (!it_is_local) emit_local_location(mod, &code, var, &offset);
-
-        WIL(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(WI_LOCAL_SET, iter_local);
-    }
-
-    emit_block(mod, &code, for_node->stmt, 0);
-
-    emit_leave_structured_block(mod, &code);
-
-    WIL(WI_LOCAL_GET, ptr_local);
-    WIL(WI_PTR_CONST, elem_size);
-    WI(WI_PTR_ADD);
-    WIL(WI_LOCAL_SET, ptr_local);
-
-    if (bh_arr_last(code).type != WI_JUMP)
-        WID(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_done_bool  = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
-    WIL(WI_LOCAL_SET, iterator_close_func);
-    WIL(WI_LOCAL_SET, iterator_next_func);
-    WIL(WI_LOCAL_SET, iterator_data_ptr);
-
-    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);
-
-    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);
-    emit_enter_structured_block(mod, &code, SBT_Continue_Loop);
-
-    if (!it_is_local) emit_local_location(mod, &code, var, &offset);
-
-    {
-        WIL(WI_LOCAL_GET, iterator_data_ptr);
-        WIL(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(WI_GLOBAL_GET, stack_top_idx);
-        WID(WI_PTR_CONST, reserve_size);
-        WI(WI_PTR_ADD);
-        WID(WI_GLOBAL_SET, stack_top_idx);
-
-        i32 type_idx = generate_type_idx(mod, next_func_type.type);
-        WID(WI_CALL_INDIRECT, ((WasmInstructionData) { type_idx, 0x00 }));
-
-        WID(WI_GLOBAL_GET, stack_top_idx);
-        WID(WI_PTR_CONST, reserve_size);
-        WI(WI_PTR_SUB);
-        WID(WI_GLOBAL_SET, stack_top_idx);
-
-        WID(WI_GLOBAL_GET, stack_top_idx);
-        emit_load_instruction(mod, &code, return_type, reserve_size - return_size);
-    }
-
-    WIL(WI_LOCAL_SET, iterator_done_bool);
-
-    if (!it_is_local) emit_store_instruction(mod, &code, var->type, offset);
-    else              WIL(WI_LOCAL_SET, iter_local);
-
-    WIL(WI_LOCAL_GET, iterator_done_bool);
-    WI(WI_I32_EQZ);
-    WID(WI_COND_JUMP, 0x01);
-
-    emit_block(mod, &code, for_node->stmt, 0);
-    WID(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);
-
-    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);
-
-    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(WI_DROP); WI(WI_DROP); WI(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, "Invalid for loop type. You should probably not be seeing this...");
-    }
-
-    local_free(mod->local_alloc, (AstTyped *) var);
-
-    *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);
-
-    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);
-
-        bh_imap_put(&block_map, (u64) sc->block, block_num);
-        block_num++;
-    }
-
-    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);
-    }
-
-    // CLEANUP: 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(WI_BLOCK_START, 0x40);
-    emit_expression(mod, &code, switch_node->expr);
-    if (switch_node->min_case != 0) {
-        WID(WI_I32_CONST, switch_node->min_case);
-        WI(WI_I32_SUB);
-    }
-    WIP(WI_JUMP_TABLE, bt);
-    WI(WI_BLOCK_END);
-
-    bh_arr_each(AstSwitchCase, sc, switch_node->cases) {
-        if (bh_imap_get(&block_map, (u64) sc->block) == 0xdeadbeef) continue;
-
-        u64 bn = bh_imap_get(&block_map, (u64) sc->block);
-
-        emit_block(mod, &code, sc->block, 0);
-
-        if (bh_arr_last(code).type != WI_JUMP)
-            WID(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),
-        .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),
-        .instructions = deferred_code,
-        .instruction_count = code_count,
-    }));
-}
-
-EMIT_FUNC(deferred_stmt, DeferredStmt deferred_stmt) {
-    bh_arr(WasmInstruction) code = *pcode;
-
-    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) {
-                bh_arr_push(code, 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);
-    }
-}
-
-// 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_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(WI_I32_CONST, 0x00);
-                emit_expression(mod, &code, unop->expr);
-                WI(WI_I32_SUB);
-
-            }
-            else if (type->kind == Basic_Kind_I64) {
-                WID(WI_I64_CONST, 0x00);
-                emit_expression(mod, &code, unop->expr);
-                WI(WI_I64_SUB);
-
-            }
-            else {
-                emit_expression(mod, &code, unop->expr);
-
-                if (type->kind == Basic_Kind_F32) WI(WI_F32_NEG);
-                if (type->kind == Basic_Kind_F64) WI(WI_F64_NEG);
-            }
-
-            break;
-        }
-
-        case Unary_Op_Not:
-            emit_expression(mod, &code, unop->expr);
-
-            WI(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(WI_I32_CONST, 0xff);
-                WI(WI_I32_XOR);
-            }
-            else if (type->kind == Basic_Kind_I16 || type->kind == Basic_Kind_U16) {
-                WID(WI_I32_CONST, 0xffff);
-                WI(WI_I32_XOR);
-            }
-            else if (type->kind == Basic_Kind_I32 || type->kind == Basic_Kind_U32) {
-                WID(WI_I32_CONST, 0xffffffff);
-                WI(WI_I32_XOR);
-            }
-            else if (type->kind == Basic_Kind_I64 || type->kind == Basic_Kind_U64) {
-                WIL(WI_I64_CONST, 0xffffffffffffffff);
-                WI(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);
-
-    // 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(WI_GLOBAL_GET, stack_top_idx);
-    WIL(WI_LOCAL_TEE, stack_top_store_local);
-    WID(WI_PTR_CONST, 0);                           // This will be filled in later.
-    WI(WI_PTR_ADD);
-    WID(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 (place_on_stack) WIL(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(WI_LOCAL_GET, stack_top_store_local);
-                WID(WI_PTR_CONST, reserve_size);
-                WI(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;
-            }
-
-            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(WI_LOCAL_GET, stack_top_store_local);
-                WIL(WI_LOCAL_GET, stack_top_store_local);
-                WID(WI_PTR_CONST, vararg_any_offsets[i]);
-                WI(WI_PTR_ADD);
-                emit_store_instruction(mod, &code, &basic_types[Basic_Kind_Rawptr], vararg_offset + i * any_size);
-
-                WIL(WI_LOCAL_GET, stack_top_store_local);
-                WID(WI_I32_CONST, vararg_any_types[i]);
-                emit_store_instruction(mod, &code, &basic_types[Basic_Kind_Type_Index], vararg_offset + i * any_size + 8);
-
-                reserve_size += any_size;
-            }
-
-            // fallthrough
-        }
-
-        case VA_Kind_Typed: {
-            WIL(WI_LOCAL_GET, stack_top_store_local);
-            if (vararg_offset > 0) {
-                WID(WI_PTR_CONST, vararg_offset);
-                WI(WI_PTR_ADD);
-            }
-            WID(WI_I32_CONST, vararg_count);
-            break;
-        }
-
-        case VA_Kind_Untyped: {
-            WIL(WI_LOCAL_GET, stack_top_store_local);
-            WIL(WI_LOCAL_GET, stack_top_store_local);
-            if (vararg_offset > 0) {
-                WID(WI_PTR_CONST, vararg_offset);
-                WI(WI_PTR_ADD);
-            }
-            emit_store_instruction(mod, &code, &basic_types[Basic_Kind_Rawptr], reserve_size);
-
-            // NOTE: There will be 4 uninitialized bytes here, because pointers are only 4 bytes in WASM.
-
-            WIL(WI_LOCAL_GET, stack_top_store_local);
-            WID(WI_I32_CONST, vararg_count);
-            emit_store_instruction(mod, &code, &basic_types[Basic_Kind_I32], reserve_size + 8);
-
-            WIL(WI_LOCAL_GET, stack_top_store_local);
-            if (reserve_size > 0) {
-                WID(WI_PTR_CONST, reserve_size);
-                WI(WI_PTR_ADD);
-            }
-
-            reserve_size += 12;
-            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);
-        bh_arr_push(code, ((WasmInstruction){ WI_CALL, func_idx }));
-
-    } else {
-        emit_expression(mod, &code, call->callee);
-
-        i32 type_idx = generate_type_idx(mod, call->callee->type);
-        WID(WI_CALL_INDIRECT, ((WasmInstructionData) { type_idx, 0x00 }));
-    }
-
-    if (reserve_size > 0) {
-        WIL(WI_LOCAL_GET,  stack_top_store_local);
-        WID(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(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, \
-                        "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(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, "SIMD lane instructions expect a compile time lane number."); \
-        *pcode = code; \
-        return; \
-    } \
-    WID(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, "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(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(WI_MEMORY_SIZE, 0x00); break;
-        case ONYX_INTRINSIC_MEMORY_GROW:  WID(WI_MEMORY_GROW, 0x00); break;
-        case ONYX_INTRINSIC_MEMORY_COPY:
-            if (context.options->use_post_mvp_features) {
-                WIL(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(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_ZERO_VALUE: {
-            // NOTE: This probably will not have to make an allocation.
-            Type* zero_type = type_build_from_ast(context.ast_alloc, (AstType *) ((AstArgument *) call->original_args.values[0])->value);
-            emit_zero_value_for_type(mod, &code, zero_type, call->token);
-            break;
-        }
-
-        case ONYX_INTRINSIC_I32_CLZ:      WI(WI_I32_CLZ); break;
-        case ONYX_INTRINSIC_I32_CTZ:      WI(WI_I32_CTZ); break;
-        case ONYX_INTRINSIC_I32_POPCNT:   WI(WI_I32_POPCNT); break;
-        case ONYX_INTRINSIC_I32_AND:      WI(WI_I32_AND); break;
-        case ONYX_INTRINSIC_I32_OR:       WI(WI_I32_OR); break;
-        case ONYX_INTRINSIC_I32_XOR:      WI(WI_I32_XOR); break;
-        case ONYX_INTRINSIC_I32_SHL:      WI(WI_I32_SHL); break;
-        case ONYX_INTRINSIC_I32_SLR:      WI(WI_I32_SHR_U); break;
-        case ONYX_INTRINSIC_I32_SAR:      WI(WI_I32_SHR_S); break;
-        case ONYX_INTRINSIC_I32_ROTL:     WI(WI_I32_ROTL); break;
-        case ONYX_INTRINSIC_I32_ROTR:     WI(WI_I32_ROTR); break;
-
-        case ONYX_INTRINSIC_I64_CLZ:      WI(WI_I64_CLZ); break;
-        case ONYX_INTRINSIC_I64_CTZ:      WI(WI_I64_CTZ); break;
-        case ONYX_INTRINSIC_I64_POPCNT:   WI(WI_I64_POPCNT); break;
-        case ONYX_INTRINSIC_I64_AND:      WI(WI_I64_AND); break;
-        case ONYX_INTRINSIC_I64_OR:       WI(WI_I64_OR); break;
-        case ONYX_INTRINSIC_I64_XOR:      WI(WI_I64_XOR); break;
-        case ONYX_INTRINSIC_I64_SHL:      WI(WI_I64_SHL); break;
-        case ONYX_INTRINSIC_I64_SLR:      WI(WI_I64_SHR_U); break;
-        case ONYX_INTRINSIC_I64_SAR:      WI(WI_I64_SHR_S); break;
-        case ONYX_INTRINSIC_I64_ROTL:     WI(WI_I64_ROTL); break;
-        case ONYX_INTRINSIC_I64_ROTR:     WI(WI_I64_ROTR); break;
-
-        case ONYX_INTRINSIC_F32_ABS:      WI(WI_F32_ABS); break;
-        case ONYX_INTRINSIC_F32_CEIL:     WI(WI_F32_CEIL); break;
-        case ONYX_INTRINSIC_F32_FLOOR:    WI(WI_F32_FLOOR); break;
-        case ONYX_INTRINSIC_F32_TRUNC:    WI(WI_F32_TRUNC); break;
-        case ONYX_INTRINSIC_F32_NEAREST:  WI(WI_F32_NEAREST); break;
-        case ONYX_INTRINSIC_F32_SQRT:     WI(WI_F32_SQRT); break;
-        case ONYX_INTRINSIC_F32_MIN:      WI(WI_F32_MIN); break;
-        case ONYX_INTRINSIC_F32_MAX:      WI(WI_F32_MAX); break;
-        case ONYX_INTRINSIC_F32_COPYSIGN: WI(WI_F32_COPYSIGN); break;
-
-        case ONYX_INTRINSIC_F64_ABS:      WI(WI_F64_ABS); break;
-        case ONYX_INTRINSIC_F64_CEIL:     WI(WI_F64_CEIL); break;
-        case ONYX_INTRINSIC_F64_FLOOR:    WI(WI_F64_FLOOR); break;
-        case ONYX_INTRINSIC_F64_TRUNC:    WI(WI_F64_TRUNC); break;
-        case ONYX_INTRINSIC_F64_NEAREST:  WI(WI_F64_NEAREST); break;
-        case ONYX_INTRINSIC_F64_SQRT:     WI(WI_F64_SQRT); break;
-        case ONYX_INTRINSIC_F64_MIN:      WI(WI_F64_MIN); break;
-        case ONYX_INTRINSIC_F64_MAX:      WI(WI_F64_MAX); break;
-        case ONYX_INTRINSIC_F64_COPYSIGN: WI(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,
-                            "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(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,
-                            "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(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,
-                            "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(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(WI_I8X16_SWIZZLE); break;
-        case ONYX_INTRINSIC_I8X16_SPLAT:   WI(WI_I8X16_SPLAT); break;
-        case ONYX_INTRINSIC_I16X8_SPLAT:   WI(WI_I16X8_SPLAT); break;
-        case ONYX_INTRINSIC_I32X4_SPLAT:   WI(WI_I32X4_SPLAT); break;
-        case ONYX_INTRINSIC_I64X2_SPLAT:   WI(WI_I64X2_SPLAT); break;
-        case ONYX_INTRINSIC_F32X4_SPLAT:   WI(WI_F32X4_SPLAT); break;
-        case ONYX_INTRINSIC_F64X2_SPLAT:   WI(WI_F64X2_SPLAT); break;
-
-        case ONYX_INTRINSIC_I8X16_EQ:   WI(WI_I8X16_EQ); break;
-        case ONYX_INTRINSIC_I8X16_NEQ:  WI(WI_I8X16_NEQ); break;
-        case ONYX_INTRINSIC_I8X16_LT_S: WI(WI_I8X16_LT_S); break;
-        case ONYX_INTRINSIC_I8X16_LT_U: WI(WI_I8X16_LT_U); break;
-        case ONYX_INTRINSIC_I8X16_GT_S: WI(WI_I8X16_GT_S); break;
-        case ONYX_INTRINSIC_I8X16_GT_U: WI(WI_I8X16_GT_U); break;
-        case ONYX_INTRINSIC_I8X16_LE_S: WI(WI_I8X16_LE_S); break;
-        case ONYX_INTRINSIC_I8X16_LE_U: WI(WI_I8X16_LE_U); break;
-        case ONYX_INTRINSIC_I8X16_GE_S: WI(WI_I8X16_GE_S); break;
-        case ONYX_INTRINSIC_I8X16_GE_U: WI(WI_I8X16_GE_U); break;
-
-        case ONYX_INTRINSIC_I16X8_EQ:   WI(WI_I16X8_EQ); break;
-        case ONYX_INTRINSIC_I16X8_NEQ:  WI(WI_I16X8_NEQ); break;
-        case ONYX_INTRINSIC_I16X8_LT_S: WI(WI_I16X8_LT_S); break;
-        case ONYX_INTRINSIC_I16X8_LT_U: WI(WI_I16X8_LT_U); break;
-        case ONYX_INTRINSIC_I16X8_GT_S: WI(WI_I16X8_GT_S); break;
-        case ONYX_INTRINSIC_I16X8_GT_U: WI(WI_I16X8_GT_U); break;
-        case ONYX_INTRINSIC_I16X8_LE_S: WI(WI_I16X8_LE_S); break;
-        case ONYX_INTRINSIC_I16X8_LE_U: WI(WI_I16X8_LE_U); break;
-        case ONYX_INTRINSIC_I16X8_GE_S: WI(WI_I16X8_GE_S); break;
-        case ONYX_INTRINSIC_I16X8_GE_U: WI(WI_I16X8_GE_U); break;
-
-        case ONYX_INTRINSIC_I32X4_EQ:   WI(WI_I32X4_EQ); break;
-        case ONYX_INTRINSIC_I32X4_NEQ:  WI(WI_I32X4_NEQ); break;
-        case ONYX_INTRINSIC_I32X4_LT_S: WI(WI_I32X4_LT_S); break;
-        case ONYX_INTRINSIC_I32X4_LT_U: WI(WI_I32X4_LT_U); break;
-        case ONYX_INTRINSIC_I32X4_GT_S: WI(WI_I32X4_GT_S); break;
-        case ONYX_INTRINSIC_I32X4_GT_U: WI(WI_I32X4_GT_U); break;
-        case ONYX_INTRINSIC_I32X4_LE_S: WI(WI_I32X4_LE_S); break;
-        case ONYX_INTRINSIC_I32X4_LE_U: WI(WI_I32X4_LE_U); break;
-        case ONYX_INTRINSIC_I32X4_GE_S: WI(WI_I32X4_GE_S); break;
-        case ONYX_INTRINSIC_I32X4_GE_U: WI(WI_I32X4_GE_U); break;
-
-        case ONYX_INTRINSIC_F32X4_EQ:  WI(WI_F32X4_EQ); break;
-        case ONYX_INTRINSIC_F32X4_NEQ: WI(WI_F32X4_NEQ); break;
-        case ONYX_INTRINSIC_F32X4_LT:  WI(WI_F32X4_LT); break;
-        case ONYX_INTRINSIC_F32X4_GT:  WI(WI_F32X4_GT); break;
-        case ONYX_INTRINSIC_F32X4_LE:  WI(WI_F32X4_LE); break;
-        case ONYX_INTRINSIC_F32X4_GE:  WI(WI_F32X4_GE); break;
-
-        case ONYX_INTRINSIC_F64X2_EQ:  WI(WI_F64X2_EQ); break;
-        case ONYX_INTRINSIC_F64X2_NEQ: WI(WI_F64X2_NEQ); break;
-        case ONYX_INTRINSIC_F64X2_LT:  WI(WI_F64X2_LT); break;
-        case ONYX_INTRINSIC_F64X2_GT:  WI(WI_F64X2_GT); break;
-        case ONYX_INTRINSIC_F64X2_LE:  WI(WI_F64X2_LE); break;
-        case ONYX_INTRINSIC_F64X2_GE:  WI(WI_F64X2_GE); break;
-
-        case ONYX_INTRINSIC_V128_NOT:       WI(WI_V128_NOT); break;
-        case ONYX_INTRINSIC_V128_AND:       WI(WI_V128_AND); break;
-        case ONYX_INTRINSIC_V128_ANDNOT:    WI(WI_V128_ANDNOT); break;
-        case ONYX_INTRINSIC_V128_OR:        WI(WI_V128_OR); break;
-        case ONYX_INTRINSIC_V128_XOR:       WI(WI_V128_XOR); break;
-        case ONYX_INTRINSIC_V128_BITSELECT: WI(WI_V128_BITSELECT); break;
-
-        case ONYX_INTRINSIC_I8X16_ABS:            WI(WI_I8X16_ABS); break;
-        case ONYX_INTRINSIC_I8X16_NEG:            WI(WI_I8X16_NEG); break;
-        case ONYX_INTRINSIC_I8X16_ANY_TRUE:       WI(WI_I8X16_ANY_TRUE); break;
-        case ONYX_INTRINSIC_I8X16_ALL_TRUE:       WI(WI_I8X16_ALL_TRUE); break;
-        case ONYX_INTRINSIC_I8X16_BITMASK:        WI(WI_I8X16_BITMASK); break;
-        case ONYX_INTRINSIC_I8X16_NARROW_I16X8_S: WI(WI_I8X16_NARROW_I16X8_S); break;
-        case ONYX_INTRINSIC_I8X16_NARROW_I16X8_U: WI(WI_I8X16_NARROW_I16X8_U); break;
-        case ONYX_INTRINSIC_I8X16_SHL:            WI(WI_I8X16_SHL); break;
-        case ONYX_INTRINSIC_I8X16_SHR_S:          WI(WI_I8X16_SHR_S); break;
-        case ONYX_INTRINSIC_I8X16_SHR_U:          WI(WI_I8X16_SHR_U); break;
-        case ONYX_INTRINSIC_I8X16_ADD:            WI(WI_I8X16_ADD); break;
-        case ONYX_INTRINSIC_I8X16_ADD_SAT_S:      WI(WI_I8X16_ADD_SAT_S); break;
-        case ONYX_INTRINSIC_I8X16_ADD_SAT_U:      WI(WI_I8X16_ADD_SAT_U); break;
-        case ONYX_INTRINSIC_I8X16_SUB:            WI(WI_I8X16_SUB); break;
-        case ONYX_INTRINSIC_I8X16_SUB_SAT_S:      WI(WI_I8X16_SUB_SAT_S); break;
-        case ONYX_INTRINSIC_I8X16_SUB_SAT_U:      WI(WI_I8X16_SUB_SAT_U); break;
-        case ONYX_INTRINSIC_I8X16_MIN_S:          WI(WI_I8X16_MIN_S); break;
-        case ONYX_INTRINSIC_I8X16_MIN_U:          WI(WI_I8X16_MIN_U); break;
-        case ONYX_INTRINSIC_I8X16_MAX_S:          WI(WI_I8X16_MAX_S); break;
-        case ONYX_INTRINSIC_I8X16_MAX_U:          WI(WI_I8X16_MAX_U); break;
-        case ONYX_INTRINSIC_I8X16_AVGR_U:         WI(WI_I8X16_AVGR_U); break;
-
-        case ONYX_INTRINSIC_I16X8_ABS:                WI(WI_I16X8_ABS); break;
-        case ONYX_INTRINSIC_I16X8_NEG:                WI(WI_I16X8_NEG); break;
-        case ONYX_INTRINSIC_I16X8_ANY_TRUE:           WI(WI_I16X8_ANY_TRUE); break;
-        case ONYX_INTRINSIC_I16X8_ALL_TRUE:           WI(WI_I16X8_ALL_TRUE); break;
-        case ONYX_INTRINSIC_I16X8_BITMASK:            WI(WI_I16X8_BITMASK); break;
-        case ONYX_INTRINSIC_I16X8_NARROW_I32X4_S:     WI(WI_I16X8_NARROW_I32X4_S); break;
-        case ONYX_INTRINSIC_I16X8_NARROW_I32X4_U:     WI(WI_I16X8_NARROW_I32X4_U); break;
-        case ONYX_INTRINSIC_I16X8_WIDEN_LOW_I8X16_S:  WI(WI_I16X8_WIDEN_LOW_I8X16_S); break;
-        case ONYX_INTRINSIC_I16X8_WIDEN_HIGH_I8X16_S: WI(WI_I16X8_WIDEN_HIGH_I8X16_S); break;
-        case ONYX_INTRINSIC_I16X8_WIDEN_LOW_I8X16_U:  WI(WI_I16X8_WIDEN_LOW_I8X16_U); break;
-        case ONYX_INTRINSIC_I16X8_WIDEN_HIGH_I8X16_U: WI(WI_I16X8_WIDEN_HIGH_I8X16_U); break;
-        case ONYX_INTRINSIC_I16X8_SHL:                WI(WI_I16X8_SHL); break;
-        case ONYX_INTRINSIC_I16X8_SHR_S:              WI(WI_I16X8_SHR_S); break;
-        case ONYX_INTRINSIC_I16X8_SHR_U:              WI(WI_I16X8_SHR_U); break;
-        case ONYX_INTRINSIC_I16X8_ADD:                WI(WI_I16X8_ADD); break;
-        case ONYX_INTRINSIC_I16X8_ADD_SAT_S:          WI(WI_I16X8_ADD_SAT_S); break;
-        case ONYX_INTRINSIC_I16X8_ADD_SAT_U:          WI(WI_I16X8_ADD_SAT_U); break;
-        case ONYX_INTRINSIC_I16X8_SUB:                WI(WI_I16X8_SUB); break;
-        case ONYX_INTRINSIC_I16X8_SUB_SAT_S:          WI(WI_I16X8_SUB_SAT_S); break;
-        case ONYX_INTRINSIC_I16X8_SUB_SAT_U:          WI(WI_I16X8_SUB_SAT_U); break;
-        case ONYX_INTRINSIC_I16X8_MUL:                WI(WI_I16X8_MUL); break;
-        case ONYX_INTRINSIC_I16X8_MIN_S:              WI(WI_I16X8_MIN_S); break;
-        case ONYX_INTRINSIC_I16X8_MIN_U:              WI(WI_I16X8_MIN_U); break;
-        case ONYX_INTRINSIC_I16X8_MAX_S:              WI(WI_I16X8_MAX_S); break;
-        case ONYX_INTRINSIC_I16X8_MAX_U:              WI(WI_I16X8_MAX_U); break;
-        case ONYX_INTRINSIC_I16X8_AVGR_U:             WI(WI_I16X8_AVGR_U); break;
-
-        case ONYX_INTRINSIC_I32X4_ABS:                WI(WI_I32X4_ABS); break;
-        case ONYX_INTRINSIC_I32X4_NEG:                WI(WI_I32X4_NEG); break;
-        case ONYX_INTRINSIC_I32X4_ANY_TRUE:           WI(WI_I32X4_ANY_TRUE); break;
-        case ONYX_INTRINSIC_I32X4_ALL_TRUE:           WI(WI_I32X4_ALL_TRUE); break;
-        case ONYX_INTRINSIC_I32X4_BITMASK:            WI(WI_I32X4_BITMASK); break;
-        case ONYX_INTRINSIC_I32X4_WIDEN_LOW_I16X8_S:  WI(WI_I32X4_WIDEN_LOW_I16X8_S); break;
-        case ONYX_INTRINSIC_I32X4_WIDEN_HIGH_I16X8_S: WI(WI_I32X4_WIDEN_HIGH_I16X8_S); break;
-        case ONYX_INTRINSIC_I32X4_WIDEN_LOW_I16X8_U:  WI(WI_I32X4_WIDEN_LOW_I16X8_U); break;
-        case ONYX_INTRINSIC_I32X4_WIDEN_HIGH_I16X8_U: WI(WI_I32X4_WIDEN_HIGH_I16X8_U); break;
-        case ONYX_INTRINSIC_I32X4_SHL:                WI(WI_I32X4_SHL); break;
-        case ONYX_INTRINSIC_I32X4_SHR_S:              WI(WI_I32X4_SHR_S); break;
-        case ONYX_INTRINSIC_I32X4_SHR_U:              WI(WI_I32X4_SHR_U); break;
-        case ONYX_INTRINSIC_I32X4_ADD:                WI(WI_I32X4_ADD); break;
-        case ONYX_INTRINSIC_I32X4_SUB:                WI(WI_I32X4_SUB); break;
-        case ONYX_INTRINSIC_I32X4_MUL:                WI(WI_I32X4_MUL); break;
-        case ONYX_INTRINSIC_I32X4_MIN_S:              WI(WI_I32X4_MIN_S); break;
-        case ONYX_INTRINSIC_I32X4_MIN_U:              WI(WI_I32X4_MIN_U); break;
-        case ONYX_INTRINSIC_I32X4_MAX_S:              WI(WI_I32X4_MAX_S); break;
-        case ONYX_INTRINSIC_I32X4_MAX_U:              WI(WI_I32X4_MAX_U); break;
-
-        case ONYX_INTRINSIC_I64X2_NEG:   WI(WI_I64X2_NEG); break;
-        case ONYX_INTRINSIC_I64X2_SHL:   WI(WI_I64X2_SHL); break;
-        case ONYX_INTRINSIC_I64X2_SHR_S: WI(WI_I64X2_SHR_S); break;
-        case ONYX_INTRINSIC_I64X2_SHR_U: WI(WI_I64X2_SHR_U); break;
-        case ONYX_INTRINSIC_I64X2_ADD:   WI(WI_I64X2_ADD); break;
-        case ONYX_INTRINSIC_I64X2_SUB:   WI(WI_I64X2_SUB); break;
-        case ONYX_INTRINSIC_I64X2_MUL:   WI(WI_I64X2_MUL); break;
-
-        case ONYX_INTRINSIC_F32X4_ABS:  WI(WI_F32X4_ABS); break;
-        case ONYX_INTRINSIC_F32X4_NEG:  WI(WI_F32X4_NEG); break;
-        case ONYX_INTRINSIC_F32X4_SQRT: WI(WI_F32X4_SQRT); break;
-        case ONYX_INTRINSIC_F32X4_ADD:  WI(WI_F32X4_ADD); break;
-        case ONYX_INTRINSIC_F32X4_SUB:  WI(WI_F32X4_SUB); break;
-        case ONYX_INTRINSIC_F32X4_MUL:  WI(WI_F32X4_MUL); break;
-        case ONYX_INTRINSIC_F32X4_DIV:  WI(WI_F32X4_DIV); break;
-        case ONYX_INTRINSIC_F32X4_MIN:  WI(WI_F32X4_MIN); break;
-        case ONYX_INTRINSIC_F32X4_MAX:  WI(WI_F32X4_MAX); break;
-
-        case ONYX_INTRINSIC_F64X2_ABS:  WI(WI_F64X2_ABS); break;
-        case ONYX_INTRINSIC_F64X2_NEG:  WI(WI_F64X2_NEG); break;
-        case ONYX_INTRINSIC_F64X2_SQRT: WI(WI_F64X2_SQRT); break;
-        case ONYX_INTRINSIC_F64X2_ADD:  WI(WI_F64X2_ADD); break;
-        case ONYX_INTRINSIC_F64X2_SUB:  WI(WI_F64X2_SUB); break;
-        case ONYX_INTRINSIC_F64X2_MUL:  WI(WI_F64X2_MUL); break;
-        case ONYX_INTRINSIC_F64X2_DIV:  WI(WI_F64X2_DIV); break;
-        case ONYX_INTRINSIC_F64X2_MIN:  WI(WI_F64X2_MIN); break;
-        case ONYX_INTRINSIC_F64X2_MAX:  WI(WI_F64X2_MAX); break;
-
-        case ONYX_INTRINSIC_I32X4_TRUNC_SAT_F32X4_S: WI(WI_I32X4_TRUNC_SAT_F32X4_S); break;
-        case ONYX_INTRINSIC_I32X4_TRUNC_SAT_F32X4_U: WI(WI_I32X4_TRUNC_SAT_F32X4_U); break;
-        case ONYX_INTRINSIC_F32X4_CONVERT_I32X4_S:   WI(WI_F32X4_CONVERT_I32X4_S); break;
-        case ONYX_INTRINSIC_F32X4_CONVERT_I32X4_U:   WI(WI_F32X4_CONVERT_I32X4_U); 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(WI_PTR_CONST, sub->elem_size);
-        WI(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(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;
-    WID(WI_PTR_CONST, memres->addr);
-    *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(WI_LOCAL_GET, local_offset);
-
-    } else {
-        WIL(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(WI_LOCAL_TEE, tmp_idx);
-
-        fori (i, 0, mem_count) {
-            type_linear_member_lookup(type, i, &two);
-            if (i != 0) WIL(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(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(WI_LOCAL_SET, temp_locals[i]);
-    }
-
-    if (!location_first) WIL(WI_LOCAL_SET, loc_idx);
-
-    fori (i, 0, elem_count) {
-        type_linear_member_lookup(type, i, &two);
-
-        u64 tmp_idx = temp_locals[i]; 
-        WIL(WI_LOCAL_GET, loc_idx);
-        WIL(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(WI_LOCAL_SET, rptr_local);
-    WIL(WI_LOCAL_SET, lptr_local);
-
-    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(WI_LOCAL_GET, lptr_local);
-
-            WIL(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(WI_LOCAL_GET, lptr_local);
-        WIL(WI_PTR_CONST, offset);
-        WI(WI_PTR_ADD);
-
-        WIL(WI_LOCAL_GET, rptr_local);
-        WIL(WI_I32_CONST, elem_count * elem_size);
-        WI(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(WI_PTR_CONST, 0);
-        WIL(WI_LOCAL_SET, offset_local);
-
-        WID(WI_BLOCK_START, 0x40);
-        WID(WI_LOOP_START, 0x40);
-            WIL(WI_LOCAL_GET, offset_local);
-            WIL(WI_LOCAL_GET, lptr_local);
-            WI(WI_PTR_ADD);
-
-            WIL(WI_LOCAL_GET, offset_local);
-            WIL(WI_LOCAL_GET, rptr_local);
-            WI(WI_PTR_ADD);
-
-            emit_load_instruction(mod, &code, elem_type, 0);
-            emit_store_instruction(mod, &code, elem_type, offset);
-
-            WIL(WI_LOCAL_GET, offset_local);
-            WIL(WI_PTR_CONST, elem_size);
-            WI(WI_PTR_ADD);
-            WIL(WI_LOCAL_TEE, offset_local);
-
-            WIL(WI_PTR_CONST, elem_count * elem_size);
-            WI(WI_PTR_GE);
-            WID(WI_COND_JUMP, 0x01);
-
-            WID(WI_JUMP, 0x00);
-
-        WI(WI_LOOP_END);
-        WI(WI_BLOCK_END);
-
-        local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
-    }
-
-    local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
-    local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
-
-    *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(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(WI_LOCAL_GET, mod->stack_base_idx);
-    WIL(WI_PTR_CONST, local_offset);
-    WI(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 (!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(WI_LOCAL_SET, result_local);
-
-    offset = 0;
-    WI(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(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(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(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;
-
-    switch (expr->kind) {
-        case Ast_Kind_Param:
-        case Ast_Kind_Local: {
-            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;
-            WID(WI_PTR_CONST, memres->addr);
-            *offset_return = 0;
-            break;
-        }
-
-        default: {
-            onyx_report_error(expr->token->pos, "Unable to generate locate 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(WI_PTR_CONST, offset);
-        WI(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->type_id != 0) {
-            WID(WI_I32_CONST, ((AstType *) expr)->type_id);
-        } else {
-            Type* t = type_build_from_ast(context.ast_alloc, type);
-            WID(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(WI_LOCAL_GET, localidx + idx);
-
-                    } else {
-                        WIL(WI_LOCAL_GET, localidx);
-                    }
-                    break;
-                }
-
-                case Param_Pass_By_Implicit_Pointer: {
-                    WIL(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(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(WI_PTR_CONST, offset);
-                    WI(WI_PTR_ADD);
-                }
-            }
-
-            break;
-        }
-
-        case Ast_Kind_Global: {
-            i32 globalidx = (i32) bh_imap_get(&mod->index_map, (u64) expr);
-
-            WID(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;
-            }
-
-            bh_arr_push(code, instr);
-            break;
-        }
-
-        case Ast_Kind_StrLit: {
-            WID(WI_PTR_CONST, ((AstStrLit *) expr)->addr);
-            WID(WI_I32_CONST, ((AstStrLit *) expr)->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(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;
-            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;
-                    WIL(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(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(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(WI_LOCAL_SET, temporaries[i]);
-                    }
-
-                    fori (i, 0, idx) WI(WI_DROP);
-
-                    fori (i, 0, field_linear_members) {
-                        type_linear_member_lookup(field->type, i, &two);
-
-                        WIL(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(WI_DROP);
-            WIL(WI_LOCAL_SET, hi_local);
-            WIL(WI_LOCAL_TEE, lo_local);
-            if (sl->elem_size != 1) {
-                WID(WI_I32_CONST, sl->elem_size);
-                WI(WI_I32_MUL);
-            }
-            emit_expression(mod, &code, sl->addr);
-            WI(WI_I32_ADD);
-            WIL(WI_LOCAL_GET, hi_local);
-            WIL(WI_LOCAL_GET, lo_local);
-            WI(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(WI_I32_CONST, so->size);
-            break;
-        }
-
-        case Ast_Kind_Align_Of: {
-            AstAlignOf* ao = (AstAlignOf *) expr;
-            WID(WI_I32_CONST, ao->alignment);
-            break;
-        }
-
-        case Ast_Kind_Enum_Value: {
-            AstEnumValue* ev = (AstEnumValue *) expr;
-            WasmType backing_type = onyx_type_to_wasm_type(ev->type);
-            if      (backing_type == WASM_TYPE_INT32) WID(WI_I32_CONST, ev->value->value.i);
-            else if (backing_type == WASM_TYPE_INT64) WID(WI_I64_CONST, ev->value->value.l);
-            else onyx_report_error(ev->token->pos, "Invalid backing type for enum.");
-            break;
-        }
-
-        case Ast_Kind_Memres: {
-            AstMemRes* memres = (AstMemRes *) expr;
-            WID(WI_I32_CONST, memres->addr);
-            emit_load_instruction(mod, &code, memres->type, 0);
-            break;
-        }
-
-        case Ast_Kind_File_Contents: {
-            AstFileContents* fc = (AstFileContents *) expr;
-
-            assert(fc->addr > 0);
-            assert(fc->size > 0);
-
-            WID(WI_PTR_CONST, fc->addr);
-            WID(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;
-        }
-
-        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(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_NOP,         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_NOP,         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(WI_DROP);
-        *pcode = code;
-        return;
-    }
-
-    if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_Array) {
-        WID(WI_I32_CONST, from->Array.count);
-        *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;
-    }
-
-    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(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(WI_LOCAL_SET, dest_loc);
-
-        } else if (mod->curr_cc == CC_Return_Stack) {
-            WIL(WI_LOCAL_GET, mod->stack_base_idx);
-            WID(WI_I32_CONST, type_size_of(ret->expr->type));
-            WI(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);
-
-    // 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--;
-        }
-    }
-
-    i64 jump_label = get_structured_jump_label(mod, Jump_Type_Return, 1);
-    if (jump_label >= 0) {
-        WIL(WI_JUMP, jump_label);
-
-    } else {
-        // Make a patch for the two instructions needed to restore the stack pointer
-        SUBMIT_PATCH(mod->stack_leave_patches, 0);
-        WI(WI_NOP);
-        WI(WI_NOP);
-
-        WI(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(WI_I32_CONST, 0); break;
-        case WASM_TYPE_INT64:   WIL(WI_I64_CONST, 0); break;
-        case WASM_TYPE_FLOAT32: WIL(WI_F32_CONST, 0); break;
-        case WASM_TYPE_FLOAT64: WIL(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(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(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, "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);
-    *(t++) = (char) return_type;
-    *t = '\0';
-
-    i32 type_idx = 0;
-    if (bh_table_has(i32, mod->type_map, type_repr_buf)) {
-        type_idx = bh_table_get(i32, mod->type_map, type_repr_buf);
-    } 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;
-
-        // HACK ish thing
-        memcpy(type->param_types, type_repr_buf, type->param_count);
-
-        bh_arr_push(mod->types, type);
-
-        bh_table_put(i32, 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 inline b32 should_emit_function(AstFunction* fd) {
-    // NOTE: Don't output intrinsic functions
-    if (fd->flags & Ast_Flag_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->flags & Ast_Flag_Exported) {
-            return 1;
-        } else {
-            return 0;
-        }
-    }
-
-    return 1;
-}
-
-
-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 = {
-        .type_idx = type_idx,
-        .locals = {
-            .param_count = 0,
-
-            .allocated = { 0 },
-            .freed     = { 0 },
-
-            .max_stack = 0,
-            .curr_stack = 0,
-        },
-        .code = NULL,
-    };
-
-    bh_arr_new(mod->allocator, wasm_func.code, 4);
-
-    i32 func_idx = (i32) bh_imap_get(&mod->index_map, (u64) fd);
-
-    // If there is no body then don't process the code
-    if (fd->body != NULL) {
-        // 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)) {
-                        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: {
-                    bh_imap_put(&mod->local_map, (u64) param->local, localidx++ | LOCAL_IS_WASM);
-                    break;
-                }
-
-                default: assert(0);
-            }
-        }
-
-        mod->local_alloc = &wasm_func.locals;
-        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);
-
-        bh_arr_insert_end(wasm_func.code, 5);
-        fori (i, 0, 5) wasm_func.code[i] = (WasmInstruction) { WI_NOP, 0 };
-
-        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.
-            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);
-
-    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);
-
-    // NOTE: Clear the local map on exit of generating this function
-    bh_imap_clear(&mod->local_map);
-}
-
-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  = fd->foreign_module,
-        .name = fd->foreign_name,
-    };
-
-    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;
-    }
-
-    bh_table_put(WasmExport, 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 - module->foreign_global_count, glob);
-
-    if (global->flags & Ast_Flag_Global_Stack_Top)
-        module->stack_top_ptr = &module->globals[global_idx - module->foreign_global_count].initial_value[0].data.i1;
-}
-
-static void emit_foreign_global(OnyxWasmModule* module, AstGlobal* global) {
-    WasmType global_type = onyx_type_to_wasm_type(global->type);
-
-    if (global->flags & Ast_Flag_Foreign) {
-        WasmImport import = {
-            .kind = WASM_FOREIGN_GLOBAL,
-            .idx  = global_type,
-            .mod  = global->foreign_module,
-            .name = global->foreign_name,
-        };
-
-        bh_arr_push(module->imports, import);
-    }
-}
-
-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);
-
-    // Warning for having '%' in a string literal (because that probably is being used for a old print format)
-    /*
-    if (charset_contains((const char *) strdata, '%')) {
-        onyx_report_warning(strlit->token->pos, "Found string literal with '%%'");
-    }
-    */
-
-    if (bh_table_has(StrLitInfo, mod->string_literals, (char *) strdata)) {
-        StrLitInfo sti = bh_table_get(StrLitInfo, mod->string_literals, (char *) strdata);
-        strlit->addr   = sti.addr;
-        strlit->length = sti.len;
-
-        bh_free(global_heap_allocator, strdata);
-        return;
-    }
-
-    WasmDatum datum = {
-        .offset = mod->next_datum_offset,
-        .length = length,
-        .data = strdata,
-    };
-
-    strlit->addr = (u32) mod->next_datum_offset,
-    strlit->length = length;
-    mod->next_datum_offset += length;
-
-    bh_table_put(StrLitInfo, mod->string_literals, (char *) strdata, ((StrLitInfo) { strlit->addr, strlit->length }));
-
-    bh_arr_push(mod->data, datum);
-}
-
-static void emit_raw_data(OnyxWasmModule* mod, ptr data, AstTyped* node) {
-    if (!emit_raw_data_(mod, data, node)) {
-        onyx_report_error(node->token->pos,
-            "Cannot generate constant data for '%s'.",
-            onyx_ast_node_kind_string(node->kind));
-    }
-}
-
-static b32 emit_raw_data_(OnyxWasmModule* mod, ptr data, AstTyped* node) {
-    b32 retval = 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_raw_data_(mod, bh_pointer_add(data, i * elem_size), *expr);
-            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_raw_data_(mod, bh_pointer_add(data, smem.offset), sl->args.values[i]);
-        }
-
-        break;
-    }
-
-    case Ast_Kind_StrLit: {
-        AstStrLit* sl = (AstStrLit *) node;
-
-        // NOTE: This assumes the address and the length fields have been filled out
-        // by emit_string_literal.
-        u32* sdata = (u32 *) data;
-        sdata[0] = sl->addr;
-        sdata[1] = 0x00;
-        sdata[2] = sl->length;
-        break;
-    }
-
-    case Ast_Kind_Enum_Value: {
-        AstEnumValue* ev = (AstEnumValue *) node;
-        retval &= emit_raw_data_(mod, data, (AstTyped *) ev->value);
-        break;
-    }
-
-    case Ast_Kind_Function: {
-        AstFunction* func = (AstFunction *) node;
-        *((u32 *) data) = get_element_idx(mod, func);
-        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:
-            *((i8 *) data) = (i8) ((AstNumLit *) node)->value.i;
-            return retval;
-
-        case Basic_Kind_I16:
-        case Basic_Kind_U16:
-            *((i16 *) data) = (i16) ((AstNumLit *) node)->value.i;
-            return retval;
-
-        case Basic_Kind_I32:
-        case Basic_Kind_U32:
-            *((i32 *) data) = ((AstNumLit *) node)->value.i;
-            return retval;
-
-        case Basic_Kind_I64:
-        case Basic_Kind_U64:
-        case Basic_Kind_Rawptr:
-            *((i64 *) data) = ((AstNumLit *) node)->value.l;
-            return retval;
-
-        case Basic_Kind_F32:
-            *((f32 *) data) = ((AstNumLit *) node)->value.f;
-            return retval;
-
-        case Basic_Kind_F64:
-            *((f64 *) data) = ((AstNumLit *) node)->value.d;
-            return retval;
-
-        default: break;
-        }
-
-        //fallthrough
-    }
-
-    case Ast_Kind_Code_Block: break;
-
-    default: retval = 0;
-    }
-
-    return retval;
-}
-
-static void emit_memory_reservation(OnyxWasmModule* mod, AstMemRes* memres) {
-    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->addr = table_location;
-
-        return;
-    }
-
-    u32 offset = mod->next_datum_offset;
-    bh_align(offset, alignment);
-
-    if (memres->initial_value != NULL) {
-        u8* data = bh_alloc(global_heap_allocator, size);
-        emit_raw_data(mod, data, memres->initial_value);
-
-        WasmDatum datum = {
-            .offset = offset,
-            .length = size,
-            .data = data,
-        };
-
-        bh_arr_push(mod->data, datum);
-    }
-
-    memres->addr = offset;
-    mod->next_datum_offset = offset + size;
-}
-
-static void emit_file_contents(OnyxWasmModule* mod, AstFileContents* fc) {
-    token_toggle_end(fc->filename_token);
-
-    // 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);
-
-        char* temp_fn     = bh_alloc_array(global_scratch_allocator, char, fc->filename_token->length);
-        i32   temp_fn_len = string_process_escape_seqs(temp_fn, fc->filename_token->text, fc->filename_token->length);
-        char* filename    = lookup_included_file(temp_fn, parent_folder, 0, 0);
-        fc->filename      = bh_strdup(global_heap_allocator, filename);
-    }
-
-    token_toggle_end(fc->filename_token);
-
-    if (bh_table_has(StrLitInfo, mod->loaded_file_info, fc->filename)) {
-        StrLitInfo info = bh_table_get(StrLitInfo, mod->loaded_file_info, fc->filename);
-        fc->addr = info.addr;
-        fc->size = info.len;
-        return;
-    }
-
-    u32 offset = mod->next_datum_offset;
-    bh_align(offset, 16);
-
-    if (!bh_file_exists(fc->filename)) {
-        onyx_report_error(fc->filename_token->pos,
-                "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);
-
-    bh_table_put(StrLitInfo, mod->loaded_file_info, fc->filename, ((StrLitInfo) {
-        .addr = offset,
-        .len  = length - 1,
-    }));
-
-    fc->addr = offset;
-    fc->size = length - 1;
-
-    WasmDatum datum = {
-        .offset = offset,
-        .length = length,
-        .data = actual_data,
-    };
-
-    bh_arr_push(mod->data, datum);
-
-    mod->next_datum_offset = offset + length;
-}
-
-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,
-        .next_datum_offset = 32, // Starting offset so null pointers don't immediately
-                                 // break constant data.       - brendanfh 2020/12/16
-
-        .elems = NULL,
-        .next_elem_idx = 0,
-
-        .structured_jump_target = NULL,
-        .return_location_stack = NULL,
-        .local_allocations = NULL,
-        .stack_leave_patches = NULL,
-        .deferred_stmts = NULL,
-
-        .stack_top_ptr = NULL,
-        .stack_base_idx = 0,
-
-        .foreign_function_count = 0,
-        .foreign_global_count = 0,
-
-        .null_proc_func_idx = -1,
-    };
-
-    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(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);
-
-    bh_table_init(global_heap_allocator, module.type_map, 61);
-    bh_table_init(global_heap_allocator, module.exports, 61);
-    bh_table_init(global_heap_allocator, module.loaded_file_info, 7);
-    bh_table_init(global_heap_allocator, module.string_literals, 16);
-
-    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);
-
-    WasmExport mem_export = {
-        .kind = WASM_FOREIGN_MEMORY,
-        .idx = 0,
-    };
-    // :ArbitraryConstant
-    // :WasmMemory
-    bh_table_put(WasmExport, module.exports, "memory", mem_export);
-    module.export_count++;
-
-    return module;
-}
-
-void emit_entity(Entity* ent) {
-    OnyxWasmModule* module = context.wasm_module;
-
-    if (module->stack_top_ptr) {
-        *module->stack_top_ptr = module->next_datum_offset;
-
-        if (*module->stack_top_ptr % 16 != 0) {
-            *module->stack_top_ptr += 16 - (*module->stack_top_ptr % 16);
-        }
-
-        builtin_heap_start.value.i = *module->stack_top_ptr + (1 << 20);
-        if (builtin_heap_start.value.i % 16 != 0) {
-            builtin_heap_start.value.i += 16 - (builtin_heap_start.value.i % 16);
-        }
-    }
-
-    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);
-            }
-            break;
-
-        case Entity_Type_Foreign_Global_Header:
-            module->foreign_global_count++;
-            emit_foreign_global(module, ent->global);
-            // fallthrough
-
-        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);
-            }
-            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_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);
-    bh_table_free(module->type_map);
-    bh_table_free(module->exports);
-}
-
-
-#include "onyxwasm_output.c"
diff --git a/src/onyxwasm_intrinsics.c b/src/onyxwasm_intrinsics.c
deleted file mode 100644 (file)
index b39fad8..0000000
+++ /dev/null
@@ -1,153 +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(WI_LOCAL_SET, count_local);
-    WIL(WI_LOCAL_SET, source_local);
-    WIL(WI_LOCAL_SET, dest_local);
-    
-    // count is greater than 0
-    WIL(WI_LOCAL_GET, count_local);
-    WID(WI_I32_CONST, 0);
-    WI(WI_I32_GT_S);
-    
-    WID(WI_IF_START, 0x40);
-    WID(WI_LOOP_START, 0x40);
-    
-    WIL(WI_LOCAL_GET, count_local);
-    WID(WI_I32_CONST, 1);
-    WI(WI_I32_SUB);
-    WIL(WI_LOCAL_SET, count_local);
-    
-    WIL(WI_LOCAL_GET, dest_local);
-    WIL(WI_LOCAL_GET, count_local);
-    WI(WI_PTR_ADD);
-    
-    WIL(WI_LOCAL_GET, source_local);
-    WIL(WI_LOCAL_GET, count_local);
-    WI(WI_PTR_ADD);
-    
-    WID(WI_I32_LOAD_8_U, ((WasmInstructionData) { 0, 0 }));
-    WID(WI_I32_STORE_8, ((WasmInstructionData) { 0, 0 }));
-    
-    WIL(WI_LOCAL_GET, count_local);
-    WID(WI_I32_CONST, 0);
-    WI(WI_I32_GT_S);
-    WID(WI_COND_JUMP, 0x00);
-    
-    WI(WI_LOOP_END);
-    WI(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(WI_LOCAL_SET, count_local);
-    WIL(WI_LOCAL_SET, byte_local);
-    WIL(WI_LOCAL_SET, dest_local);
-    
-    // count is greater than 0
-    WIL(WI_LOCAL_GET, count_local);
-    WID(WI_I32_CONST, 0);
-    WI(WI_I32_GT_S);
-    
-    WID(WI_IF_START, 0x40);
-    WID(WI_LOOP_START, 0x40);
-    
-    WIL(WI_LOCAL_GET, count_local);
-    WID(WI_I32_CONST, 1);
-    WI(WI_I32_SUB);
-    WIL(WI_LOCAL_SET, count_local);
-    
-    WIL(WI_LOCAL_GET, dest_local);
-    WIL(WI_LOCAL_GET, count_local);
-    WI(WI_PTR_ADD);
-    
-    WIL(WI_LOCAL_GET, byte_local);
-    WID(WI_I32_STORE_8, ((WasmInstructionData) { 0, 0 }));
-    
-    WIL(WI_LOCAL_GET, count_local);
-    WID(WI_I32_CONST, 0);
-    WI(WI_I32_GT_S);
-    WID(WI_COND_JUMP, 0x00);
-    
-    WI(WI_LOOP_END);
-    WI(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(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(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,
-                    "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;
-}
diff --git a/src/onyxwasm_output.c b/src/onyxwasm_output.c
deleted file mode 100644 (file)
index e1c5764..0000000
+++ /dev/null
@@ -1,621 +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_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_CODE 10
-#define WASM_SECTION_ID_DATA 11
-
-typedef i32 vector_func(void*, bh_buffer*);
-
-static const u8 WASM_MAGIC_STRING[] = { 0x00, 0x61, 0x73, 0x6D };
-static const u8 WASM_VERSION[] = { 0x01, 0x00, 0x00, 0x00 };
-
-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, bh_buffer* buff) {
-    i32 leb_len, prev_len = buff->length;
-    u8* leb;
-    
-    bh_buffer_write_byte(buff, (max >= 0) ? 0x01 : 0x00);
-    
-    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, &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) {
-    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);
-    
-    // FIXME: This needs to be dynamically chosen depending on the size of
-    // the data section and stack size pre-requeseted.
-    // :WasmMemory
-    output_limits(1024, -1, &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->text, import->mod->length, &vec_buff);
-        output_name(import->name->text, import->name->length, &vec_buff);
-        bh_buffer_write_byte(&vec_buff, (u8) import->kind);
-        
-        leb = uint_to_uleb128((u64) import->idx, &leb_len);
-        bh_buffer_append(&vec_buff, leb, leb_len);
-        
-        if (import->kind == WASM_FOREIGN_GLOBAL) {
-            // NOTE: All foreign globals are mutable
-            bh_buffer_write_byte(&vec_buff, 0x01);
-        }
-    }
-    
-    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;
-    bh_table_each_start(WasmExport, module->exports);
-    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);
-    bh_table_each_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_startsection(OnyxWasmModule* module, bh_buffer* buff) {
-    i32 prev_len = buff->length;
-    
-    i32 start_idx = -1;
-    bh_table_each_start(WasmExport, module->exports) {
-        if (value.kind == WASM_FOREIGN_FUNCTION) {
-            if (strncmp("main", key, 5) == 0) {
-                start_idx = value.idx;
-                break;
-            }
-        }
-    } bh_table_each_end;
-    
-    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_NOP) 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 {
-        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_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);
-    
-    // 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);
-    
-    // DEBUG_HERE;
-    
-    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_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) {
-        if (datum->data == NULL) continue;
-        
-        // NOTE: 0x00 memory index
-        bh_buffer_write_byte(&vec_buff, 0x00);
-        
-        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);
-        
-        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]);
-    }
-    
-    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;
-}
-
-void onyx_wasm_module_write_to_file(OnyxWasmModule* module, bh_file file) {
-    bh_buffer master_buffer;
-    bh_buffer_init(&master_buffer, global_heap_allocator, 128);
-    bh_buffer_append(&master_buffer, WASM_MAGIC_STRING, 4);
-    bh_buffer_append(&master_buffer, WASM_VERSION, 4);
-    
-    output_typesection(module, &master_buffer);
-    output_importsection(module, &master_buffer);
-    output_funcsection(module, &master_buffer);
-    output_tablesection(module, &master_buffer);
-    output_memorysection(module, &master_buffer);
-    output_globalsection(module, &master_buffer);
-    output_exportsection(module, &master_buffer);
-    output_startsection(module, &master_buffer);
-    output_elemsection(module, &master_buffer);
-    output_codesection(module, &master_buffer);
-    output_datasection(module, &master_buffer);
-    
-    bh_file_write(&file, master_buffer.data, master_buffer.length);
-}
diff --git a/src/onyxwasm_type_table.c b/src/onyxwasm_type_table.c
deleted file mode 100644 (file)
index 4daf9d1..0000000
+++ /dev/null
@@ -1,374 +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.
-
-
-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))
-
-    // This is the data behind the "type_table" slice in type_info.onyx
-    u32 type_count = bh_arr_length(type_map.entries) + 1;
-    u64* table_info = bh_alloc_array(global_heap_allocator, u64, type_count); // HACK
-    memset(table_info, 0, type_count * sizeof(u64));
-
-    bh_buffer table_buffer;
-    bh_buffer_init(&table_buffer, global_heap_allocator, 4096);
-
-    // 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.ptr_to_data->Pointer.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.ptr_to_data->Pointer.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.ptr_to_data->Pointer.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));
-                bh_buffer_align(&table_buffer, 8);
-                PATCH;
-                bh_buffer_write_u64(&table_buffer, components_base);
-                bh_buffer_write_u64(&table_buffer, 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);
-
-                PATCH;
-                bh_buffer_write_u64(&table_buffer, parameters_base);
-                bh_buffer_write_u64(&table_buffer, 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);
-                    PATCH;
-                    bh_buffer_write_u64(&table_buffer, name_loc);
-                    bh_buffer_write_u64(&table_buffer, (*value)->token->length);
-                    bh_buffer_write_u64(&table_buffer, (*value)->value->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);
-                PATCH;
-                bh_buffer_write_u64(&table_buffer, name_base);
-                bh_buffer_write_u64(&table_buffer, name_length);
-                PATCH;
-                bh_buffer_write_u64(&table_buffer, member_base);
-                bh_buffer_write_u64(&table_buffer, 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);
-                memset(value_locations, 0, s->mem_count * sizeof(u32));
-
-                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);
-
-                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);
-                            u8* buffer = table_buffer.data + table_buffer.length;
-                            emit_raw_data(module, buffer, sln->value);
-                            table_buffer.length += size;
-                            break;
-                        }
-
-                        default: {
-                            // Set to null if this is not known how to encode
-                            param_locations[i-1] = 0;
-                            break;
-                        }
-                    }
-                }
-
-                bh_buffer_align(&table_buffer, 8);
-
-                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);
-                    u8* buffer = table_buffer.data + table_buffer.length;
-
-                    if (!emit_raw_data_(module, buffer, value)) {
-                        // 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;
-                    }
-                }
-
-                bh_buffer_align(&table_buffer, 8);
-                u32 members_base = table_buffer.length;
-
-                i = 0;
-                bh_arr_each(StructMember*, pmem, s->memarr) {
-                    StructMember* mem = *pmem;
-
-                    u32 name_loc = name_locations[i];
-                    u32 value_loc = value_locations[i++];
-
-                    bh_buffer_align(&table_buffer, 8);
-                    PATCH;
-                    bh_buffer_write_u64(&table_buffer, name_loc);
-                    bh_buffer_write_u64(&table_buffer, 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);
-                    
-                    bh_buffer_align(&table_buffer, 8);
-                    PATCH;
-                    bh_buffer_write_u64(&table_buffer, value_loc);
-                }
-
-                bh_buffer_align(&table_buffer, 8);
-                u32 params_base = table_buffer.length;
-
-                i = 0;
-                bh_arr_each(AstPolySolution, sln, s->poly_sln) {
-                    bh_buffer_align(&table_buffer, 8);
-                    PATCH;
-                    bh_buffer_write_u64(&table_buffer, 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);
-                }
-
-                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));
-                bh_buffer_write_u32(&table_buffer, 0);
-                PATCH;
-                bh_buffer_write_u64(&table_buffer, name_base);
-                bh_buffer_write_u64(&table_buffer, name_length);
-                PATCH;
-                bh_buffer_write_u64(&table_buffer, members_base);
-                bh_buffer_write_u64(&table_buffer, s->mem_count);
-                PATCH;
-                bh_buffer_write_u64(&table_buffer, params_base);
-                bh_buffer_write_u64(&table_buffer, bh_arr_length(s->poly_sln));
-
-                break;
-            }
-        }
-    }
-
-    u32 offset = module->next_datum_offset;
-    bh_align(offset, 8);
-
-    u64 type_table_location = offset;
-
-    WasmDatum type_table_data = {
-        .offset = offset,
-        .length = type_count * 8,
-        .data = table_info,
-    };
-    bh_arr_push(module->data, type_table_data);
-
-    offset += type_table_data.length;
-
-    fori (i, 0, type_count) {
-        table_info[i] += offset;
-    }
-
-    bh_arr_each(u32, patch_loc, base_patch_locations) {
-        u64* loc = bh_pointer_add(table_buffer.data, *patch_loc);
-        if (*loc == 0) continue;
-        
-        *loc += offset;
-    }
-
-    WasmDatum type_info_data = {
-        .offset = offset,
-        .length = table_buffer.length,
-        .data = table_buffer.data,
-    };
-    bh_arr_push(module->data, type_info_data);
-    offset += type_info_data.length;
-
-    u64 global_data_ptr = offset;
-
-    u64* tmp_data = bh_alloc(global_heap_allocator, 16);
-    tmp_data[0] = type_table_location;
-    tmp_data[1] = type_count;
-    WasmDatum type_table_global_data = {
-        .offset = offset,
-        .length = 16,
-        .data = tmp_data,
-    };
-    bh_arr_push(module->data, type_table_global_data);
-    offset += type_table_global_data.length;
-
-    module->next_datum_offset = offset;
-
-    return global_data_ptr;
-
-#undef PATCH
-}
diff --git a/src/parser.c b/src/parser.c
new file mode 100644 (file)
index 0000000..ced1fdc
--- /dev/null
@@ -0,0 +1,2862 @@
+// 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 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);
+static AstNode*       parse_statement(OnyxParser* parser);
+static AstType*       parse_type(OnyxParser* parser);
+static AstTypeOf*     parse_typeof(OnyxParser* parser);
+static AstStructType* parse_struct(OnyxParser* parser);
+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(OnyxParser* parser, AstTyped** ret);
+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 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, "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;
+}
+
+
+// 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;
+
+    // NOTE: Hex literals are unsigned.
+    if (int_node->token->length >= 2 && int_node->token->text[1] == 'x') {
+        if ((u64) value >= ((u64) 1 << 32))
+            int_node->type_node = (AstType *) &basic_type_u64;
+        else
+            int_node->type_node = (AstType *) &basic_type_u32;
+    } else {
+        int_node->type_node = (AstType *) &basic_type_int_unsized;
+    }
+
+    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;
+
+        if (next_tokens_are(parser, 2, Token_Type_Symbol, '=')) {
+            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)) break;
+            if (parse_possible_quick_function_definition(parser, &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: {
+            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->addr      = 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, "Expected '{' after 'do', got '%s'.", token_name(parser->curr->type));
+                retval = NULL;
+                break;
+            }
+
+            do_block->block = parse_block(parser, 1);
+
+            retval = (AstTyped *) do_block;
+            break;
+        }
+
+        // :TypeValueInterchange
+        case '<': {
+            AstTypeAlias* alias = make_node(AstTypeAlias, Ast_Kind_Type_Alias);
+            alias->token = expect_token(parser, '<');
+            alias->to = parse_type(parser);
+            expect_token(parser, '>');
+
+            retval = (AstTyped *) alias;
+            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_token = expect_token(parser, Token_Type_Literal_String);
+                fc->type = type_make_slice(parser->allocator, &basic_types[Basic_Kind_U8]);
+
+                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->addr  = 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, "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 = (AstPolyProc *) 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 = parse_type(parser);
+
+                    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, "code")) {
+                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);
+                    ((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 (parse_possible_directive(parser, "insert")) {
+                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;
+            }
+
+            onyx_report_error(parser->curr->pos, "Invalid directive in expression.");
+            return NULL;
+        }
+
+        default:
+        no_match:
+            onyx_report_error(parser->curr->pos, "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 '(': {
+                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_Right_Arrow: {
+                AstBinaryOp* method_call = make_node(AstBinaryOp, Ast_Kind_Method_Call);
+                method_call->token = expect_token(parser, Token_Type_Right_Arrow);
+                method_call->left = retval;
+                method_call->right = parse_factor(parser);
+
+                retval = (AstTyped *) method_call;
+                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;
+
+                // nocheckin This should maybe be goto factor_parsed; ??
+                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;
+
+        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;
+        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_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);
+
+    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);
+
+        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);
+        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;
+
+    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);
+
+    if (consume_token_if_next(parser, Token_Type_Keyword_Else)) {
+        while_node->false_stmt = parse_block(parser, 1);
+    }
+
+    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 (consume_token_if_next(parser, '^')) {
+        for_node->by_pointer = 1;
+    }
+
+    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, ':');
+    for_node->iter = parse_expression(parser, 1);
+    for_node->stmt = parse_block(parser, 1);
+
+    return for_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);
+
+    bh_arr_new(global_heap_allocator, switch_node->cases, 4);
+
+    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;
+
+    expect_token(parser, '{');
+
+    while (consume_token_if_next(parser, Token_Type_Keyword_Case)) {
+        if (parser->hit_unexpected_token) return switch_node;
+
+        bh_arr(AstTyped *) case_values = NULL;
+        bh_arr_new(global_heap_allocator, case_values, 1);
+
+        if (parse_possible_directive(parser, "default")) {
+            switch_node->default_case = parse_block(parser, 1);
+
+            if (parser->curr->type != '}') {
+                onyx_report_error(parser->curr->pos, "The #default case must be the last case in a switch statement.\n");
+            }
+            break;
+        }
+
+        AstTyped* value = parse_expression(parser, 1);
+        bh_arr_push(case_values, value);
+        while (consume_token_if_next(parser, ',')) {
+            if (parser->hit_unexpected_token) return switch_node;
+
+            value = parse_expression(parser, 1);
+            bh_arr_push(case_values, value);
+        }
+
+        AstBlock* block = parse_block(parser, 1);
+
+        AstSwitchCase sc_node;
+        sc_node.block  = block;
+        sc_node.values = case_values;
+
+        bh_arr_push(switch_node->cases, sc_node);
+    }
+
+    expect_token(parser, '}');
+    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 != ',') 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);
+        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;
+
+        AstNode* sym_node = make_symbol(parser->allocator, local_sym);
+        bh_arr_push(local_compound->exprs, (AstTyped *) sym_node);
+
+        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 == ',') {
+        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 == ':') {
+        AstBinding* binding = parse_top_level_binding(parser, symbol);
+        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.symbol_name = expect_token(parser, Token_Type_Symbol);
+
+            if (consume_token_if_next(parser, Token_Type_Keyword_As))
+                qu.as_name = expect_token(parser, Token_Type_Symbol);
+            else
+                qu.as_name = qu.symbol_name;
+
+            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);
+            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);
+            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_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")) {
+                AstLocal* context_tmp = make_local(parser->allocator, NULL, builtin_context_variable->type_node);
+
+                AstBinaryOp* assignment = make_node(AstBinaryOp, Ast_Kind_Binary_Op);
+                assignment->operation = Binary_Op_Assign;
+                assignment->left = (AstTyped *) context_tmp;
+                assignment->right = builtin_context_variable;
+                context_tmp->next = (AstNode *) assignment;
+
+                AstBinaryOp* assignment2 = make_node(AstBinaryOp, Ast_Kind_Binary_Op);
+                assignment2->operation = Binary_Op_Assign;
+                assignment2->left = builtin_context_variable;
+                assignment2->right = (AstTyped *) context_tmp;
+
+                AstBlock* context_block = parse_block(parser, 1);
+                assignment->next = (AstNode *) context_block;
+
+                AstDefer* defer_node = make_node(AstDefer, Ast_Kind_Defer);
+                defer_node->stmt = (AstNode *) assignment2;
+                defer_node->next = context_block->body;
+                context_block->body = (AstNode *) defer_node;
+
+                needs_semicolon = 0;
+                retval = (AstNode *) context_tmp;
+                break;
+            }
+
+            if (next_tokens_are(parser, 2, '#', Token_Type_Keyword_If)) {
+                AstIf* static_if = parse_static_if_stmt(parser, 1);
+                ENTITY_SUBMIT(static_if);
+
+                needs_semicolon = 0;
+                retval = (AstNode *) static_if;
+                break;
+            }
+
+            if (parse_possible_directive(parser, "persist")) {
+                // :Duplicated from parse_top_level_statement
+                AstMemRes* memres = make_node(AstMemRes, Ast_Kind_Memres);
+                memres->token = expect_token(parser, Token_Type_Symbol);
+                expect_token(parser, ':');
+
+                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);
+
+                AstBinding* binding = make_node(AstBinding, Ast_Kind_Binding);
+                binding->token = memres->token;
+                binding->node = (AstNode *) memres;
+                ENTITY_SUBMIT(binding);
+                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) {
+    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);
+        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, "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 Token_Type_Keyword_Proc: {
+                OnyxToken* proc_token = expect_token(parser, Token_Type_Keyword_Proc);
+                onyx_report_warning(proc_token->pos, "Warning: 'proc' is a deprecated keyword.");
+                *next_insertion = parse_function_type(parser, proc_token);
+                next_insertion = NULL;
+                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 == '(') {
+                    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_type(parser);
+                        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;
+            }
+
+            case '#': {
+                // :ValueDirectiveHack
+                if (parse_possible_directive(parser, "value")) {
+                // It is very weird to put these here.
+                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:
+                    *next_insertion = (AstType *) parse_expression(parser, 0);
+                    next_insertion = NULL;
+                    break;
+                }
+
+                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, "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 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;
+
+    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;
+    }
+
+    bh_arr_new(global_heap_allocator, s_node->members, 4);
+
+    while (parser->curr->type == '#') {
+        if (parser->hit_unexpected_token) return NULL;
+
+        if (parse_possible_directive(parser, "union")) {
+            s_node->flags |= Ast_Flag_Struct_Is_Union;
+        }
+
+        else if (parse_possible_directive(parser, "align")) {
+            AstNumLit* numlit = parse_int_literal(parser);
+            if (numlit == NULL) return NULL;
+
+            s_node->min_alignment = numlit->value.i;
+        }
+
+        else if (parse_possible_directive(parser, "size")) {
+            AstNumLit* numlit = parse_int_literal(parser);
+            if (numlit == NULL) return NULL;
+
+            s_node->min_size = numlit->value.i;
+        }
+
+        else {
+            OnyxToken* directive_token = expect_token(parser, '#');
+            OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
+
+            onyx_report_error(directive_token->pos, "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;
+
+        member_is_used = consume_token_if_next(parser, Token_Type_Keyword_Use);
+
+        if (next_tokens_are(parser, 3, Token_Type_Symbol, ':', ':')) {
+            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* 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, ';');
+
+        } else {
+            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, "'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, "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;
+
+                if (member_is_used) mem->flags |= Ast_Flag_Struct_Mem_Used;
+
+                bh_arr_push(s_node->members, mem);
+            }
+
+            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 {
+        return s_node;
+    }
+}
+
+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);
+
+    b32 param_use = 0;
+    b32 param_is_baked = 0;
+    OnyxToken* symbol;
+    while (!consume_token_if_next(parser, ')')) {
+        if (parser->hit_unexpected_token) return;
+
+        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);
+        expect_token(parser, ':');
+
+        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.local->flags |= Ast_Flag_Param_Use;
+            param_use = 0;
+        }
+
+        if (parser->curr->type != '=') {
+            if (consume_token_if_next(parser, Token_Type_Dot_Dot)) {
+                if (consume_token_if_next(parser, '.')) curr_param.vararg_kind = VA_Kind_Untyped;
+                else                                    curr_param.vararg_kind = VA_Kind_Typed;
+            }
+
+            if (curr_param.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);
+                curr_param.local->type_node = parse_type(parser);
+                i32 new_len = bh_arr_length(*parser->polymorph_context.poly_params);
+
+                if (curr_param.vararg_kind == VA_Kind_Typed) {
+                    AstVarArgType* va_type = make_node(AstVarArgType, Ast_Kind_VarArg_Type);
+                    va_type->elem = curr_param.local->type_node;
+                    va_type->token = curr_param.local->type_node->token;
+                    curr_param.local->type_node = (AstType *) va_type;
+                }
+
+                fori (i, 0, new_len - old_len) {
+                    (*parser->polymorph_context.poly_params)[old_len + i].type_expr = curr_param.local->type_node;
+                    (*parser->polymorph_context.poly_params)[old_len + i].idx = param_idx;
+                }
+            }
+        }
+
+        if (curr_param.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;
+                curr_param.default_value = (AstTyped *) cs;
+
+            } else {
+                curr_param.default_value = parse_expression(parser, 0);
+            }
+        }
+
+        if (param_is_baked) {
+            param_is_baked = 0;
+
+            bh_arr(AstPolyParam) pv = *parser->polymorph_context.poly_params;
+            bh_arr_push(pv, ((AstPolyParam) {
+                .kind = PPK_Baked_Value,
+                .idx = param_idx,
+
+                .poly_sym = (AstNode *) curr_param.local,
+                .type_expr = curr_param.local->type_node,
+            }));
+
+            *parser->polymorph_context.poly_params = pv;
+        }
+
+        bh_arr_push(func->params, curr_param);
+        param_idx++;
+
+        if (parser->curr->type != ')')
+            expect_token(parser, ',');
+    }
+    return;
+}
+
+static AstOverloadedFunction* parse_overloaded_function(OnyxParser* parser, OnyxToken* token) {
+    expect_token(parser, '{');
+
+    AstOverloadedFunction* ofunc = make_node(AstOverloadedFunction, Ast_Kind_Overloaded_Function);
+    ofunc->token = token;
+    ofunc->flags |= Ast_Flag_Comptime;
+
+    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_new(global_heap_allocator, func_def->params, 4);
+
+    bh_arr(AstPolyParam) polymorphic_vars = NULL;
+    bh_arr_new(global_heap_allocator, polymorphic_vars, 4);
+
+    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;
+    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);
+        }
+    }
+
+    while (parser->curr->type == '#') {
+        if (parse_possible_directive(parser, "intrinsic")) {
+            func_def->flags |= Ast_Flag_Intrinsic;
+
+            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->flags |= Ast_Flag_Foreign;
+        }
+
+        // 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, "unknown directive '#%b'.", symbol_token->text, symbol_token->length);
+        }
+    }
+
+    func_def->body = parse_block(parser, 1);
+
+    if (bh_arr_length(polymorphic_vars) > 0) {
+        AstPolyProc* pp = make_node(AstPolyProc, Ast_Kind_Polymorphic_Proc);
+        pp->token = func_def->token;
+        pp->poly_params = polymorphic_vars;
+        pp->base_func = func_def;
+
+        return (AstFunction *) pp;
+
+    } else {
+        bh_arr_free(polymorphic_vars);
+        return func_def;
+    }
+}
+
+static b32 parse_possible_function_definition(OnyxParser* parser, AstTyped** ret) {
+    if (parser->curr->type == '(') {
+        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_Type_Right_Arrow
+            && token_after_paren->type != '{'
+            && token_after_paren->type != Token_Type_Keyword_Do
+            && token_after_paren->type != Token_Type_Empty_Block)
+            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;
+
+        OnyxToken* proc_token = parser->curr;
+        AstFunction* func_node = parse_function_definition(parser, proc_token);
+        ENTITY_SUBMIT(func_node);
+        *ret = (AstTyped *) func_node;
+        return 1;
+    }
+
+    return 0;
+}
+
+static b32 parse_possible_quick_function_definition(OnyxParser* parser, AstTyped** ret) {
+    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;
+
+    OnyxToken* proc_token = expect_token(parser, '(');
+
+    bh_arr(OnyxToken*) params=NULL;
+    bh_arr_new(global_heap_allocator, params, 4);
+
+    while (parser->curr->type != ')') {
+        if (parser->hit_unexpected_token) return 0;
+
+        bh_arr_push(params, expect_token(parser, Token_Type_Symbol));
+
+        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(OnyxToken*, param, params) {
+        char text[512];
+        memset(text, 0, 512);
+        strncat(text, "__type_", 511);
+        token_toggle_end(*param);
+        strncat(text, (*param)->text, 511);
+        token_toggle_end(*param);
+
+        OnyxToken* new_token = bh_alloc(parser->allocator, sizeof(OnyxToken));
+        new_token->type = Token_Type_Symbol;
+        new_token->length = 7 + (*param)->length;
+        new_token->text = bh_strdup(parser->allocator, text);
+        new_token->pos = (*param)->pos;
+
+        AstNode* type_node = make_symbol(parser->allocator, new_token);
+        bh_arr_push(poly_params, type_node);
+    }
+
+    AstFunction* func_node = make_node(AstFunction, Ast_Kind_Function);
+    AstPolyProc* poly_proc = make_node(AstPolyProc, Ast_Kind_Polymorphic_Proc);
+
+    bh_arr_new(global_heap_allocator, func_node->params, bh_arr_length(params));
+    fori (i, 0, bh_arr_length(params)) {
+        AstLocal* param_local = make_local(parser->allocator, params[i], (AstType *) poly_params[i]);
+        param_local->kind = Ast_Kind_Param;
+
+        bh_arr_push(func_node->params, ((AstParam) {
+            .local = param_local,
+            .default_value = NULL,
+
+            .vararg_kind = 0,
+            .use_processed = 0,
+        }));
+    }
+
+    AstBlock* body_block;
+    AstType*  return_type;
+
+    if (parser->curr->type == '{') {
+        body_block = parse_block(parser, 1);
+        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;
+    }
+
+    func_node->token = proc_token;
+    func_node->body = body_block;
+    func_node->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,
+        }));
+    }
+    poly_proc->base_func = func_node;
+
+    ENTITY_SUBMIT(poly_proc);
+    *ret = (AstTyped *) poly_proc;
+
+    bh_arr_free(params);
+    bh_arr_free(poly_params);
+    return 1;
+}
+
+static AstTyped* parse_global_declaration(OnyxParser* parser) {
+    AstGlobal* global_node = make_node(AstGlobal, Ast_Kind_Global);
+    global_node->token = expect_token(parser, Token_Type_Keyword_Global);
+
+    while (parser->curr->type == '#') {
+        if (parse_possible_directive(parser, "foreign")) {
+            global_node->foreign_module = expect_token(parser, Token_Type_Literal_String);
+            global_node->foreign_name   = expect_token(parser, Token_Type_Literal_String);
+
+            global_node->flags |= Ast_Flag_Foreign;
+        }
+
+        else {
+            OnyxToken* directive_token = expect_token(parser, '#');
+            OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
+
+            onyx_report_error(directive_token->pos, "unknown directive '#%b'.", symbol_token->text, symbol_token->length);
+        }
+    }
+
+    global_node->type_node = parse_type(parser);
+
+    ENTITY_SUBMIT(global_node);
+
+    return (AstTyped *) global_node;
+}
+
+static AstEnumType* parse_enum_declaration(OnyxParser* 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 (parse_possible_directive(parser, "flags")) {
+            enum_node->flags |= Ast_Flag_Enum_Is_Flags;
+        } else {
+            OnyxToken* directive_token = expect_token(parser, '#');
+            OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
+
+            onyx_report_error(directive_token->pos, "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, ':');
+
+            // TODO: Make this work for any expression.
+            evalue->value = parse_int_literal(parser);
+        }
+
+        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) {
+    AstIf* static_if_node = make_node(AstIf, Ast_Kind_Static_If);
+    static_if_node->token = expect_token(parser, '#');
+    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);
+
+    } 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);
+
+        } 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 AstMacro* parse_macro(OnyxParser* parser) {
+    AstMacro* macro = make_node(AstMacro, Ast_Kind_Macro);
+    macro->token = expect_token(parser, Token_Type_Keyword_Macro);
+    
+    // First try quick function
+    if (!parse_possible_quick_function_definition(parser, &macro->body)) {
+        // Otherwise, do a normal function
+        macro->body  = (AstTyped *) parse_function_definition(parser, macro->token);
+    }
+
+    ENTITY_SUBMIT(macro);
+    return macro;
+}
+
+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_Enum)   return (AstTyped *) parse_enum_declaration(parser);
+    if (parser->curr->type == Token_Type_Keyword_Macro)  return (AstTyped *) parse_macro(parser);
+
+    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;
+    }
+
+    return parse_expression(parser, 1);
+}
+
+static AstBinding* parse_top_level_binding(OnyxParser* parser, OnyxToken* symbol) {
+    expect_token(parser, ':');
+
+    AstTyped* node = parse_top_level_expression(parser);
+    if (parser->hit_unexpected_token || node == NULL)
+        return NULL;
+
+    // CLEANUP
+    if (node->kind == Ast_Kind_Function) {
+        AstFunction* func = (AstFunction *) node;
+
+        if (func->intrinsic_name == NULL)
+            func->intrinsic_name = symbol;
+
+        func->name = symbol;
+
+    } else if (node->kind == Ast_Kind_Polymorphic_Proc) {
+        AstPolyProc* proc = (AstPolyProc *) node;
+
+        if (proc->base_func->intrinsic_name == NULL)
+            proc->base_func->intrinsic_name = symbol;
+
+        proc->base_func->name = symbol;
+
+    } else if (node->kind == Ast_Kind_Macro) {
+        AstMacro* macro = (AstMacro *) node;
+
+        AstFunction* func = (AstFunction *) macro->body;
+        if (func->kind == Ast_Kind_Polymorphic_Proc)
+            func = (AstFunction *) ((AstPolyProc *) func)->base_func;
+
+        func->name = symbol;
+
+    } else if (node->kind == Ast_Kind_Global) {
+        AstGlobal* global = (AstGlobal *) node;
+
+        global->name = symbol;
+
+    } else if (node->kind != Ast_Kind_Overloaded_Function
+            && node->kind != Ast_Kind_StrLit) {
+
+        if (node->kind == Ast_Kind_Struct_Type
+                || node->kind == Ast_Kind_Enum_Type
+                || node->kind == Ast_Kind_Poly_Struct_Type) {
+            ((AstStructType *)node)->name = bh_aprintf(global_heap_allocator,
+                "%b", symbol->text, symbol->length);
+        }
+
+        if (node->kind == Ast_Kind_Type_Alias)    node->token = symbol;
+        else if (node_is_type((AstNode *) node));
+        else if (node->kind == Ast_Kind_Package);
+        else if (node->kind == Ast_Kind_NumLit);
+        else {
+            AstAlias* alias = make_node(AstAlias, Ast_Kind_Alias);
+            alias->token = node->token;
+            alias->alias = node;
+            node = (AstTyped *) alias;
+        }
+
+        // HACK: This should maybe be entered elsewhere?
+        ENTITY_SUBMIT(node);
+    }
+
+    AstBinding* binding = make_node(AstBinding, Ast_Kind_Binding);
+    binding->token = symbol;
+    binding->node = (AstNode *) node;
+
+    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 (parse_possible_directive(parser, "private")) {
+        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, "private_file")) {
+        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);
+            expect_token(parser, ':');
+
+            if (parser->curr->type == ':') {
+                binding = parse_top_level_binding(parser, symbol);
+                if (binding != NULL) binding->flags |= private_kind;
+
+                goto submit_binding_to_entities;
+            }
+
+            AstMemRes* memres = make_node(AstMemRes, Ast_Kind_Memres);
+            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);
+
+            binding = make_node(AstBinding, Ast_Kind_Binding);
+            binding->token = symbol;
+            binding->flags |= private_kind;
+            binding->node = (AstNode *) memres;
+
+            goto submit_binding_to_entities;
+        }
+
+        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;
+
+                OnyxToken* str_token = expect_token(parser, Token_Type_Literal_String);
+                if (str_token != NULL) {
+                    token_toggle_end(str_token);
+                    include->name = bh_strdup(parser->allocator, str_token->text);
+                    token_toggle_end(str_token);
+                }
+
+                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;
+
+                OnyxToken* str_token = expect_token(parser, Token_Type_Literal_String);
+                if (str_token != NULL) {
+                    token_toggle_end(str_token);
+                    include->name = bh_strdup(parser->allocator, str_token->text);
+                    token_toggle_end(str_token);
+                }
+
+                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, "operator")) {
+                AstDirectiveOperator *operator = make_node(AstDirectiveOperator, Ast_Kind_Directive_Operator);
+                operator->token = dir_token;
+
+                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, "Invalid binary operator.");
+                } else {
+                    operator->operator = op;
+                }
+
+                operator->overload = parse_expression(parser, 0);
+
+                ENTITY_SUBMIT(operator);
+                return;
+            }
+            else if (parse_possible_directive(parser, "add_overload") || parse_possible_directive(parser, "add_match")) {
+                AstDirectiveAddOverload *add_overload = make_node(AstDirectiveAddOverload, Ast_Kind_Directive_Add_Overload);
+                add_overload->token = dir_token;
+                add_overload->overloaded_function = (AstNode *) parse_expression(parser, 0);
+
+                expect_token(parser, ',');
+
+                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;
+                }
+
+                add_overload->overload = parse_expression(parser, 0);
+
+                ENTITY_SUBMIT(add_overload);
+                return;
+            }
+            else if (parse_possible_directive(parser, "export")) {
+                AstDirectiveExport *export = make_node(AstDirectiveExport, Ast_Kind_Directive_Export);
+                export->token = dir_token;
+                export->export_name = expect_token(parser, Token_Type_Literal_String);
+
+                export->export = parse_expression(parser, 0);
+
+                ENTITY_SUBMIT(export);
+                return;
+            }
+            else {
+                OnyxToken* directive_token = expect_token(parser, '#');
+                OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
+
+                onyx_report_error(directive_token->pos, "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);
+    }
+
+    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);
+
+        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.scope_flags = NULL;
+
+    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.scope_flags, 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;
+
+    AstUse* implicit_use_builtin = make_node(AstUse, Ast_Kind_Use);
+    AstPackage* implicit_builtin_package = make_node(AstPackage, Ast_Kind_Package);
+    implicit_builtin_package->package_name = "builtin";
+    implicit_use_builtin->expr = (AstTyped *) implicit_builtin_package;
+    ENTITY_SUBMIT(implicit_use_builtin);
+
+    parse_top_level_statements_until(parser, Token_Type_End_Stream);
+
+    parser->current_scope = parser->current_scope->parent;
+}
diff --git a/src/polymorph.c b/src/polymorph.c
new file mode 100644 (file)
index 0000000..c05734b
--- /dev/null
@@ -0,0 +1,896 @@
+
+//
+// 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;
+
+// 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 };
+
+static void ensure_polyproc_cache_is_created(AstPolyProc* pp) {
+    if (pp->concrete_funcs == NULL) {
+        bh_table_init(global_heap_allocator, pp->concrete_funcs, 16);
+    }
+}
+
+static void insert_poly_slns_into_scope(Scope* scope, bh_arr(AstPolySolution) slns) {
+    bh_arr_each(AstPolySolution, sln, slns) {
+        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;
+                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);
+    }
+}
+
+// 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, b32 header_already_processed) {
+    solidified_func.func->flags |= Ast_Flag_Function_Used;
+    solidified_func.func->flags |= Ast_Flag_From_Polymorphism;
+
+    EntityState header_start_state = Entity_State_Resolve_Symbols;
+    if (header_already_processed) header_start_state = Entity_State_Code_Gen;
+
+    Entity func_header_entity = {
+        .state = header_start_state,
+        .type = Entity_Type_Function_Header,
+        .function = solidified_func.func,
+        .package = NULL,
+        .scope = solidified_func.poly_scope,
+    };
+
+    entity_bring_to_state(&func_header_entity, Entity_State_Code_Gen);
+    if (onyx_has_errors()) return 0;
+
+    Entity func_entity = {
+        .state = Entity_State_Resolve_Symbols,
+        .type = Entity_Type_Function,
+        .function = solidified_func.func,
+        .package = NULL,
+        .scope = solidified_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->entity_header = entity_header;
+    solidified_func.func->entity_body   = 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(
+    AstPolyProc* pp,
+    bh_arr(AstPolySolution) slns,
+    OnyxToken* tkn,
+    b32 header_only) {
+
+    AstSolidifiedFunction solidified_func;
+    solidified_func.header_complete = 0;
+
+    // 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;
+
+    solidified_func.poly_scope = scope_create(context.ast_alloc, pp->poly_scope, poly_scope_pos);
+    insert_poly_slns_into_scope(solidified_func.poly_scope, slns);
+
+    if (header_only) {
+        solidified_func.func = (AstFunction *) clone_function_header(context.ast_alloc, pp->base_func);
+        solidified_func.func->flags |= Ast_Flag_Incomplete_Body;
+
+    } else {
+        solidified_func.func = (AstFunction *) ast_clone(context.ast_alloc, pp->base_func);
+    }
+
+    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(AstPolyProc* pp, AstSolidifiedFunction solidified_func) {
+    if (solidified_func.func->flags & Ast_Flag_Incomplete_Body) {
+        clone_function_body(context.ast_alloc, solidified_func.func, pp->base_func);
+
+        // 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, 1));
+
+        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_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) break;
+
+                bh_arr_push(elem_queue, ((PolySolveElem) {
+                    .type_expr = ((AstSliceType *) elem.type_expr)->elem,
+                    .kind = PSK_Type,
+                    .actual = elem.actual->Slice.ptr_to_data->Pointer.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.ptr_to_data->Pointer.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_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(AstPolyProc* pp, AstPolyParam* param, Arguments* args, char** err_msg) {
+    bh_arr(AstTyped *) arg_arr = args->values;
+    bh_arr(AstNamedValue *) named_values = args->named_values;
+
+    // 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 = pp->base_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;
+            }
+        }
+
+        // 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;
+}
+
+// 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, AstPolyProc* pp, 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(pp, param, args, err_msg);
+            if (typed_param == NULL) return;
+
+            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;
+    }
+
+    *resolved = solve_poly_type(param->poly_sym, param->type_expr, actual_type);
+}
+
+
+// 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.
+// 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, AstPolyProc* pp, 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(pp, 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;
+    }
+
+    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;
+        }
+
+        if (param->type == NULL)
+            param->type = type_build_from_ast(context.ast_alloc, param->type_expr);
+        assert(param->type);
+
+        if (!type_check_or_auto_cast(&value, param->type)) {
+            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(pp->base_func),
+                    type_get_name(param->type),
+                    param->idx + 1,
+                    bh_num_suffix(param->idx + 1),
+                    node_get_type_name(value));
+            return;
+        }
+
+        *resolved = ((PolySolveResult) { PSK_Value, value });
+    }
+
+    if (orig_value->kind == Ast_Kind_Argument) {
+        ((AstArgument *) orig_value)->is_baked = 1;
+    }
+}
+
+// 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(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual, char** err_msg) {
+    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);
+
+    bh_arr_each(AstPolyParam, param, pp->poly_params) {
+
+        // NOTE: First check to see if this polymorphic variable was already specified in the
+        // known solutions.
+        b32 already_solved = 0;
+        bh_arr_each(AstPolySolution, known_sln, pp->known_slns) {
+            if (token_equals(param->poly_sym->token, known_sln->poly_sym->token)) {
+                already_solved = 1;
+                break;
+            }
+        }
+        if (already_solved) continue;
+
+        // 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, pp, param, pp_lookup, actual, err_msg); break;
+            case PPK_Baked_Value: solve_for_polymorphic_param_value(&resolved, pp, param, pp_lookup, actual, err_msg); break;
+
+            default: if (err_msg) *err_msg = "Invalid polymorphic parameter kind. This is a compiler bug.";
+        }
+
+        if (flag_to_yield) goto sln_not_found;
+        
+        switch (resolved.kind) {
+            case PSK_Undefined:
+                // 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);
+
+                goto sln_not_found;
+
+            case PSK_Type:
+                bh_arr_push(slns, ((AstPolySolution) {
+                    .kind     = PSK_Type,
+                    .poly_sym = param->poly_sym,
+                    .type     = resolved.actual,
+                }));
+                break;
+
+            case PSK_Value:
+                bh_arr_push(slns, ((AstPolySolution) {
+                    .kind     = PSK_Value,
+                    .poly_sym = param->poly_sym,
+                    .value    = resolved.value,
+                }));
+                break;
+        }
+    }
+
+    return slns;
+
+    sln_not_found:
+    bh_arr_free(slns);
+    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(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual, OnyxToken* tkn) {
+    ensure_polyproc_cache_is_created(pp);
+
+    char *err_msg = NULL;
+    bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, pp_lookup, actual, &err_msg);
+    if (slns == NULL) {
+        if (flag_to_yield) {
+            flag_to_yield = 0;
+            return (AstFunction *) &node_that_signals_a_yield;
+        }
+
+        if (err_msg != NULL) onyx_report_error(tkn->pos, err_msg);
+        else                 onyx_report_error(tkn->pos, "Some kind of error occured when generating a polymorphic procedure. You hopefully will not see this");
+
+        return NULL;
+    }
+
+    AstFunction* result = polymorphic_proc_solidify(pp, slns, tkn);
+    
+    bh_arr_free(slns);
+    return result;
+}
+
+AstFunction* polymorphic_proc_solidify(AstPolyProc* 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);
+    if (bh_table_has(AstSolidifiedFunction, pp->concrete_funcs, unique_key)) {
+        AstSolidifiedFunction solidified_func = bh_table_get(AstSolidifiedFunction, pp->concrete_funcs, unique_key);
+
+        // 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);
+
+    // NOTE: Cache the function for later use, reducing duplicate functions.
+    bh_table_put(AstSolidifiedFunction, pp->concrete_funcs, unique_key, solidified_func);
+
+    if (!add_solidified_function_entities(solidified_func, 0)) {
+        onyx_report_error(tkn->pos, "Error in polymorphic procedure header generated from this call site.");
+        return NULL;
+    }
+
+    return solidified_func.func;
+}
+
+// NOTE: This can return either a AstFunction or an AstPolyProc, depending if enough parameters were
+// supplied to remove all the polymorphic variables from the function.
+AstNode* polymorphic_proc_try_solidify(AstPolyProc* 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 {
+            onyx_report_error(tkn->pos, "'%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
+        AstPolyProc* new_pp = onyx_ast_node_new(context.ast_alloc, sizeof(AstPolyProc), Ast_Kind_Polymorphic_Proc);
+        new_pp->token = pp->token;                            // TODO: Change this to be the solidify->token
+        new_pp->base_func = pp->base_func;
+        new_pp->flags = pp->flags;
+        new_pp->poly_params = 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(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual) {
+    bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, pp_lookup, actual, NULL);
+    if (slns == NULL) return NULL;
+
+    ensure_polyproc_cache_is_created(pp);
+
+    AstSolidifiedFunction solidified_func;
+
+    char* unique_key = build_poly_slns_unique_key(slns);
+    if (bh_table_has(AstSolidifiedFunction, pp->concrete_funcs, unique_key)) {
+        solidified_func = bh_table_get(AstSolidifiedFunction, pp->concrete_funcs, unique_key);
+
+    } 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.header_complete) return solidified_func.func;
+
+    Entity func_header_entity = {
+        .state = Entity_State_Resolve_Symbols,
+        .type = Entity_Type_Function_Header,
+        .function = solidified_func.func,
+        .package = NULL,
+        .scope = solidified_func.poly_scope,
+    };
+
+    // HACK: Everything with entity_bring_to_state is a big hack...
+    // The idea is that instead of making the caller yield for a simple procedure header,
+    // do a single pass of trying to solve for the parameter types and return types.
+    // The big missing piece of the entity pumping system is that once an entity is
+    // entered into the system, it cannot be removed and it is assumed it is going to
+    // be used in the final binary. This is obviously not always the case, but that
+    // is how the system currently works.
+    b32 successful = entity_bring_to_state(&func_header_entity, Entity_State_Code_Gen);
+    if (onyx_has_errors()) {
+        onyx_errors_print();
+        onyx_clear_errors();
+        return NULL;
+    }
+
+    solidified_func.header_complete = successful;
+
+    // NOTE: Cache the function for later use, only if it didn't have errors in its header.
+    bh_table_put(AstSolidifiedFunction, pp->concrete_funcs, unique_key, solidified_func);
+    
+    return solidified_func.func;
+}
+
+//
+// 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);
+}
+
+AstStructType* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(AstPolySolution) slns, OnyxFilePos pos) {
+    // @Cleanup
+    assert(ps_type->scope != NULL);
+
+    if (ps_type->concrete_structs == NULL) {
+        bh_table_init(global_heap_allocator, ps_type->concrete_structs, 16);
+    }
+
+    if (bh_arr_length(slns) != bh_arr_length(ps_type->poly_params)) {
+        onyx_report_error(pos, "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];
+        
+        PolySolutionKind expected_kind = PSK_Undefined;
+        if ((AstNode *) ps_type->poly_params[i].type_node == (AstNode *) &basic_type_type_expr) {
+            expected_kind = PSK_Type;
+        } else {
+            expected_kind = PSK_Value;
+        }
+
+        if (sln->kind != expected_kind) {
+            if (expected_kind == PSK_Type) 
+                onyx_report_error(pos, "Expected type expression for %d%s argument.", i + 1, bh_num_suffix(i + 1));
+
+            if (expected_kind == PSK_Value)
+                onyx_report_error(pos, "Expected value expression of type '%s' for %d%s argument.",
+                    type_get_name(ps_type->poly_params[i].type),
+                    i + 1, bh_num_suffix(i + 1));
+
+            return NULL;
+        }
+
+        if (sln->kind == PSK_Value) {
+            resolve_expression_type(sln->value);
+
+            if ((sln->value->flags & Ast_Flag_Comptime) == 0) {
+                onyx_report_error(pos,
+                    "Expected compile-time known argument for '%b'.",
+                    sln->poly_sym->token->text,
+                    sln->poly_sym->token->length);
+                return NULL;
+            }
+
+            if (!types_are_compatible(sln->value->type, ps_type->poly_params[i].type)) {
+                onyx_report_error(pos, "Expected compile-time argument of type '%s', got '%s'.",
+                    type_get_name(ps_type->poly_params[i].type),
+                    type_get_name(sln->value->type));
+                return NULL;
+            }
+        }
+
+        i++;
+    }
+
+    char* unique_key = build_poly_slns_unique_key(slns);
+    if (bh_table_has(AstStructType *, ps_type->concrete_structs, unique_key)) {
+        AstStructType* concrete_struct = bh_table_get(AstStructType *, ps_type->concrete_structs, unique_key);
+
+        if (concrete_struct->entity_type->state < Entity_State_Check_Types) {
+            return NULL;
+        }
+
+        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 concrete_struct;
+    }
+
+    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);
+    bh_table_put(AstStructType *, 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
new file mode 100644 (file)
index 0000000..4e1bcef
--- /dev/null
@@ -0,0 +1,1252 @@
+#define BH_DEBUG
+#include "parser.h"
+#include "utils.h"
+#include "astnodes.h"
+#include "errors.h"
+
+// Variables used during the symbol resolution phase.
+static Scope*       curr_scope    = NULL;
+static b32 report_unresolved_symbols = 1;
+
+// 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)
+
+typedef enum SymresStatus {
+    Symres_Success,
+    Symres_Complete,
+
+    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* call);
+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_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 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) {
+            onyx_report_error(token->pos,
+                "Unable to resolve symbol '%b'",
+                token->text,
+                token->length);
+
+            return Symres_Error;
+        } else {
+            return Symres_Yield_Macro;
+        }
+
+    } else {
+        *symbol_node = res;
+    }
+
+    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;
+
+    if (s_node->scope) {
+        // FIX: This is probably wrong for the long term.
+        s_node->scope->parent = curr_scope;
+
+        scope_enter(s_node->scope);
+    }
+
+    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 (!node_is_type((AstNode *) member->type_node)) {
+                onyx_report_error(member->token->pos, "Member type is not a type.");
+                goto struct_symres_done;
+            }
+
+            if (member->flags & Ast_Flag_Struct_Mem_Used) {
+                AstType *used = (AstType *) member->type_node;
+
+                while (used->kind == Ast_Kind_Type_Alias) {
+                    used = ((AstTypeAlias *) used)->to;
+                }
+
+                b32 use_works = (used->kind == Ast_Kind_Struct_Type || used->kind == Ast_Kind_Poly_Call_Type);
+
+                if (used->kind == Ast_Kind_Type_Raw_Alias) {
+                    AstTypeRawAlias* alias = (AstTypeRawAlias *) used;
+                    use_works = (alias->to->kind == Type_Kind_Struct);
+                }
+
+                if (!use_works) {
+                    onyx_report_error(member->token->pos,
+                            "Can only 'use' members of struct type, got '%s'.",
+                            onyx_ast_node_kind_string(used->kind));
+                    goto struct_symres_done;
+                }
+            }
+        }
+    }
+
+struct_symres_done:
+    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);
+
+            if (!node_is_type((AstNode *) *type))
+                onyx_report_error((*type)->token->pos, "Field access did not result in a type. (%s)", onyx_ast_node_kind_string((*type)->kind));
+            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;
+
+            SYMRES(type, &ftype->return_type);
+
+            if (ftype->param_count > 0) {
+                fori (i, 0, (i64) ftype->param_count) {
+                    SYMRES(type, &ftype->params[i]);
+                }
+            }
+            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;
+            pst_node->scope = scope_create(context.ast_alloc, pst_node->entity->scope, pst_node->token->pos);
+
+            bh_arr_each(AstPolyStructParam, param, pst_node->poly_params) {
+                SYMRES(type, &param->type_node);
+                param->type = type_build_from_ast(context.ast_alloc, param->type_node);
+            }
+            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(type, (AstType **) &alias->alias);
+            break;
+        }
+
+        case Ast_Kind_Typeof: {
+            AstTypeOf* type_of = (AstTypeOf *) *type;
+            SYMRES(expression, &type_of->expr);
+            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* call) {
+    SYMRES(expression, (AstTyped **) &call->callee);
+    SYMRES(arguments, &call->args);
+
+    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);
+
+    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) {
+            onyx_report_error((*fa)->token->pos, "'%b' was not found in package '%s'. Perhaps it is defined in a file that wasn't loaded?",
+                (*fa)->token->text,
+                (*fa)->token->length,
+                ((AstPackage *) (*fa)->expr)->package->name);
+            return Symres_Error;
+        } else {
+            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, "Pipe operator expected call on right side.");
+        return Symres_Error;
+    }
+
+    if ((*pipe)->left == NULL) return Symres_Error;
+
+    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) {
+    AstCall* call_node = (AstCall *) (*mcall)->right;
+    if (call_node->kind != Ast_Kind_Call) {
+        onyx_report_error((*mcall)->token->pos, "'->' expected procedure call on right side.");
+        return Symres_Error;
+    }
+
+    SYMRES(expression, &(*mcall)->left);
+    if ((*mcall)->left == NULL) return Symres_Error;
+
+    if (((*mcall)->flags & Ast_Flag_Has_Been_Symres) == 0) {
+        AstFieldAccess* implicit_field_access = make_field_access(context.ast_alloc, (*mcall)->left, NULL);
+        implicit_field_access->token = call_node->callee->token;
+        call_node->callee = (AstTyped *) implicit_field_access;
+    }
+
+    SYMRES(expression, (AstTyped **) &call_node);
+    (*mcall)->flags |= Ast_Flag_Has_Been_Symres;
+
+    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);
+    SYMRES(type, (AstType **) &sl->stnode);
+
+    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);
+    SYMRES(type, (AstType **) &al->atnode);
+
+    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_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_Address_Of:   SYMRES(expression, &((AstAddressOf *)(*expr))->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_Alias:        SYMRES(expression, &((AstAlias *) *expr)->alias); break;
+
+        case Ast_Kind_Range_Literal:
+            SYMRES(expression, &((AstRangeLiteral *)(*expr))->low);
+            SYMRES(expression, &((AstRangeLiteral *)(*expr))->high);
+
+            SYMRES(type, &builtin_range_type);
+            (*expr)->type_node = builtin_range_type;
+
+            // NOTE: This is a weird place to put this so maybe put it somewhere else eventually
+            //                                                  - brendanfh   2020/09/04
+            builtin_range_type_type = type_build_from_ast(context.ast_alloc, builtin_range_type);
+            break;
+
+        case Ast_Kind_Function:
+        case Ast_Kind_NumLit:
+            SYMRES(type, &(*expr)->type_node);
+            break;
+
+        case Ast_Kind_StrLit:
+            SYMRES(type, &builtin_string_type);
+            (*expr)->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;
+
+        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) {
+            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_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);
+
+    bh_arr_each(AstSwitchCase, sc, switchnode->cases) {
+        bh_arr_each(AstTyped *, value, sc->values)
+            SYMRES(expression, value);
+
+        SYMRES(block, sc->block);
+    }
+
+    if (switchnode->default_case)
+        SYMRES(block, switchnode->default_case);
+
+    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);
+
+    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,
+                                "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);
+            }
+        }
+
+        package_track_use_package(package->package, use->entity);
+
+        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,
+                            "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;
+
+    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;
+
+        bh_table_each_start(StructMember, st->Struct.members);
+            AstFieldAccess* fa = make_field_access(context.ast_alloc, use->expr, value.name);
+            symbol_raw_introduce(curr_scope, value.name, use->token->pos, (AstNode *) fa);
+        bh_table_each_end;
+
+        return Symres_Success;
+    }
+
+cannot_use:
+    onyx_report_error(use->token->pos, "Cannot use this because its type is unknown.");
+    return Symres_Error;
+}
+
+static SymresStatus symres_directive_solidify(AstDirectiveSolidify** psolid) {
+    AstDirectiveSolidify* solid = *psolid;
+    if (solid->resolved_proc != NULL) {
+        *psolid = (AstDirectiveSolidify *) solid->resolved_proc;
+        return Symres_Success;
+    }
+
+    SYMRES(expression, (AstTyped **) &solid->poly_proc);
+    if (solid->poly_proc && solid->poly_proc->kind == Ast_Kind_Directive_Solidify) {
+        AstPolyProc* potentially_resolved_proc = (AstPolyProc *) ((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, "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);
+        if (onyx_has_errors()) return Symres_Error;
+
+        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;
+        }
+
+        if (onyx_has_errors()) return Symres_Error;
+    }
+
+    solid->resolved_proc = polymorphic_proc_try_solidify(solid->poly_proc, solid->known_polyvars, solid->token);
+
+    // NOTE: Not a DirectiveSolidify.
+    *psolid = (AstDirectiveSolidify *) solid->resolved_proc;
+    return Symres_Success;
+}
+
+static SymresStatus symres_directive_defined(AstDirectiveDefined** pdefined) {
+    AstDirectiveDefined* defined = *pdefined;
+
+    b32 use_package_count = (context.entities.type_count[Entity_Type_Use_Package] == 0);
+    b32 old_report_unresolved_symbols = report_unresolved_symbols;
+    report_unresolved_symbols = 0;
+
+    SymresStatus ss = symres_expression(&defined->expr);
+    if ((use_package_count || old_report_unresolved_symbols) && ss != Symres_Success) {
+        // The symbol definitely was not found and there is no chance that it could be found.
+        defined->is_defined = 0;
+
+    } else {
+        if (ss == Symres_Success) {
+            defined->is_defined = 1;
+
+        } else {
+            report_unresolved_symbols = old_report_unresolved_symbols;
+            return Symres_Yield_Macro;
+        }
+    }
+
+    report_unresolved_symbols = old_report_unresolved_symbols;
+    return Symres_Success;
+}
+
+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_Jump:        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)
+        SYMRES(statement_chain, &block->body);
+
+    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);
+
+    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;
+        }
+    }
+
+    if ((func->flags & Ast_Flag_Params_Introduced) == 0) {
+        bh_arr_each(AstParam, param, func->params) {
+            symbol_introduce(curr_scope, param->local->token, (AstNode *) param->local);
+        }
+
+        func->flags |= Ast_Flag_Params_Introduced;
+    }
+
+    bh_arr_each(AstParam, param, func->params) {
+        if (param->local->type_node != NULL) {
+            SYMRES(type, &param->local->type_node);
+        }
+    }
+
+    SYMRES(type, &func->return_type);
+    if (!node_is_type((AstNode *) func->return_type)) {
+        AstType* return_type = (AstType *) strip_aliases((AstNode *) func->return_type);
+        if (return_type->kind == Ast_Kind_Symbol) return Symres_Yield_Macro;
+
+        onyx_report_error(func->token->pos, "Return type is not a type.");
+    }
+
+    scope_leave();
+
+    return Symres_Success;
+}
+
+SymresStatus symres_function(AstFunction* func) {
+    if (func->scope == NULL)
+        func->scope = scope_create(context.ast_alloc, curr_scope, func->token->pos);
+    if (func->entity_header && func->entity_header->state < Entity_State_Check_Types) return Symres_Yield_Macro;
+
+    scope_enter(func->scope);
+
+    if ((func->flags & Ast_Flag_Has_Been_Symres) == 0) {
+        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->local->flags & Ast_Flag_Param_Use) != 0 && param->use_processed == 0) {
+                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) {
+                        // HACK HACK HACK
+                        scope_leave();
+                        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;
+                    }
+
+                    bh_table_each_start(StructMember, st->Struct.members);
+                        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);
+                    bh_table_each_end;
+
+                    param->use_processed = 1;
+
+                } else if (param->local->type != NULL) {
+                    onyx_report_error(param->local->token->pos, "Can only 'use' structures or pointers to structures.");
+
+                } else {
+                    // :ExplicitTyping
+                    onyx_report_error(param->local->token->pos, "Cannot deduce type of parameter '%b'; Try adding it explicitly.",
+                        param->local->token->text,
+                        param->local->token->length);
+                }
+            }
+        }
+
+        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,
+                    "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;
+
+    enum_node->backing_type = type_build_from_ast(context.ast_alloc, enum_node->backing);
+    enum_node->scope = scope_create(context.ast_alloc, NULL, enum_node->token->pos);
+
+    type_build_from_ast(context.ast_alloc, (AstType *) enum_node);
+
+    u64 next_assign_value = (enum_node->flags & Ast_Flag_Enum_Is_Flags) ? 1 : 0;
+    bh_arr_each(AstEnumValue *, value, enum_node->values) {
+        symbol_introduce(enum_node->scope, (*value)->token, (AstNode *) *value);
+        (*value)->type = enum_node->etcache;
+
+        if ((*value)->value != NULL) {
+            // HACK
+            resolve_expression_type((AstTyped *) (*value)->value);
+            if (type_is_small_integer((*value)->value->type)) {
+                next_assign_value = (*value)->value->value.i;
+            } else if (type_is_integer((*value)->value->type)) {
+                next_assign_value = (*value)->value->value.l;
+            } else {
+                onyx_report_error((*value)->token->pos, "expected numeric integer literal for enum initialization");
+                return Symres_Error;
+            }
+
+            (*value)->value->type = enum_node->etcache;
+
+        } else {
+            AstNumLit* num = make_int_literal(context.ast_alloc, next_assign_value);
+            num->type = enum_node->etcache;
+
+            (*value)->value = num;
+        }
+
+        (*value)->flags |= Ast_Flag_Comptime;
+
+        if (enum_node->flags & Ast_Flag_Enum_Is_Flags) {
+            next_assign_value <<= 1;
+        } else {
+            next_assign_value++;
+        }
+    }
+    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);
+
+    bh_arr_each(AstStructMember *, smem, st->members) {
+        if ((*smem)->initial_value != NULL) {
+            SYMRES(expression, &(*smem)->initial_value);
+        }
+    }
+
+    if (st->scope) scope_leave();
+    return Symres_Success;
+}
+
+static SymresStatus symres_polyproc(AstPolyProc* pp) {
+    pp->flags |= Ast_Flag_Comptime;
+    pp->poly_scope = curr_scope;
+
+    bh_arr_each(AstPolyParam, param, pp->poly_params) {
+        if (param->kind != PPK_Baked_Value) continue;
+
+        // FIX: Looking up symbols immediately in the type of the baked value does not always work
+        // because I think the following should be possible:
+        //
+        //     baked_proc :: (x: $T, $f: (T) -> T) -> T ...
+        //
+        // The type of 'f' depends on resolving the value for the polyvar 'T'.
+        SYMRES(type, &param->type_expr);
+    }
+
+    return Symres_Success;
+}
+
+static SymresStatus symres_static_if(AstIf* static_if) {
+    SYMRES(expression, &static_if->cond);
+    return Symres_Success;
+}
+
+static SymresStatus symres_process_directive(AstNode* directive) {
+    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, "#add_overload directive did not resolve to an overloaded function.");
+
+            } else {
+                AstOverloadedFunction* ofunc = (AstOverloadedFunction *) add_overload->overloaded_function;
+                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, "This cannot be used as an operator overload.");
+                return Symres_Error;
+            }
+
+            if (bh_arr_length(overload->params) != 2) {
+                onyx_report_error(operator->token->pos, "Expected 2 exactly arguments for binary operator overload.");
+                return Symres_Error;
+            }
+
+            /*if (binop_is_assignment(operator->operator)) {
+                onyx_report_error(overload->token->pos, "'%s' is not currently overloadable.", binaryop_string[operator->operator]);
+                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);
+
+            export->export->flags |= Ast_Flag_Exported;
+
+            if (export->export->kind == Ast_Kind_Function) {
+                AstFunction *func = (AstFunction *) export->export;
+                func->exported_name = export->export_name;
+
+                if ((func->flags & Ast_Flag_Exported) != 0) {
+                    if ((func->flags & Ast_Flag_Foreign) != 0) {
+                        onyx_report_error(export->token->pos, "exporting a foreign function");
+                        return Symres_Error;
+                    }
+
+                    if ((func->flags & Ast_Flag_Intrinsic) != 0) {
+                        onyx_report_error(export->token->pos, "exporting a intrinsic function");
+                        return Symres_Error;
+                    }
+
+                    // NOTE: This should never happen
+                    if (func->exported_name == NULL) {
+                        onyx_report_error(export->token->pos, "exporting function without a name");
+                        return Symres_Error;
+                    }
+                }
+            }
+
+            break;
+        }
+    }
+
+    return Symres_Success;
+}
+
+static SymresStatus symres_macro(AstMacro* macro) {
+    if (macro->body->kind == Ast_Kind_Function) {
+        SYMRES(function_header, (AstFunction *) macro->body);
+    }
+    else if (macro->body->kind == Ast_Kind_Polymorphic_Proc) {
+        SYMRES(polyproc, (AstPolyProc *) macro->body);
+    }
+
+    return Symres_Success;
+}
+
+void symres_entity(Entity* ent) {
+    Scope* old_scope = NULL;
+    if (ent->scope) {
+        old_scope = curr_scope;
+        scope_enter(ent->scope);
+    }
+
+    report_unresolved_symbols = context.cycle_detected;
+                                //(context.entities.type_count[Entity_Type_Static_If] == 0 &&
+                                // context.entities.type_count[Entity_Type_Use_Package] == 0)
+                                //|| 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_Foreign_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_Foreign_Global_Header:
+        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_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_Polymorphic_Proc:        ss = symres_polyproc(ent->poly_proc); 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;
+
+        default: break;
+    }
+
+    if (ss == Symres_Yield_Macro) ent->macro_attempts++;
+    if (ss == Symres_Yield_Micro) ent->micro_attempts++;
+    if (ss == Symres_Success) {
+        ent->macro_attempts = 0;
+        ent->micro_attempts = 0;
+        ent->state = next_state;
+    }
+
+    if (ent->scope) curr_scope = old_scope;
+}
diff --git a/src/types.c b/src/types.c
new file mode 100644 (file)
index 0000000..84ccbd0
--- /dev/null
@@ -0,0 +1,1268 @@
+#define BH_DEBUG
+#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,                    8,  8, "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_slice_map;
+static bh_imap type_dynarr_map;
+static bh_imap type_vararg_map;
+static bh_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_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);
+    bh_table_init(global_heap_allocator, type_func_map, 64);
+
+    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;
+
+                    if (t1_struct->Struct.memarr[0]->used)
+                        return types_are_compatible(t2_struct, t1_struct->Struct.memarr[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.ptr_to_data->Pointer.elem, t2->Slice.ptr_to_data->Pointer.elem);
+        }
+
+        case Type_Kind_VarArgs: {
+            if (t2->kind != Type_Kind_VarArgs) return 0;
+            return types_are_compatible(t1->VarArgs.ptr_to_data->Pointer.elem, t2->VarArgs.ptr_to_data->Pointer.elem);
+        }
+
+        case Type_Kind_DynArray: {
+            if (t2->kind != Type_Kind_DynArray) return 0;
+            return types_are_compatible(t1->DynArray.ptr_to_data->Pointer.elem, t2->DynArray.ptr_to_data->Pointer.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;
+        }
+
+        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 8;
+        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 16; // HACK: These should not have to be 16 bytes in size, they should only have to be 12,
+        case Type_Kind_VarArgs:  return 16; // 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 32; // data (8), count (4), capacity (4), allocator { func (4), ---(4), data (8) }
+        case Type_Kind_Compound: return type->Compound.size;
+        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 8;
+        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 8;
+        case Type_Kind_VarArgs:  return 8;
+        case Type_Kind_DynArray: return 8;
+        case Type_Kind_Compound: return 4; // HACK
+        default: return 1;
+    }
+}
+
+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: {
+            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 (bh_table_has(u64, type_func_map, name)) {
+                u64 id = bh_table_get(u64, type_func_map, name);
+                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);
+            bh_table_put(u64, type_func_map, name, func_type->id);
+
+            return func_type;
+        }
+
+        case Ast_Kind_Array_Type: {
+            AstArrayType* a_node = (AstArrayType *) type_node;
+
+            Type* a_type = type_create(Type_Kind_Array, alloc, 0);
+            a_type->Array.elem = type_build_from_ast(alloc, a_node->elem);
+
+            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((AstTyped *) 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, "Array type expects type 'i32' for size, got '%s'.",
+                        type_get_name(a_node->count_expr->type));
+                    return NULL;
+                }
+
+                count = get_expression_integer_value((AstTyped *) a_node->count_expr);
+            }
+
+            a_type->Array.count = count;
+            a_type->Array.size = type_size_of(a_type->Array.elem) * count;
+
+            type_register(a_type);
+            return a_type;
+        }
+
+        case Ast_Kind_Struct_Type: {
+            AstStructType* s_node = (AstStructType *) type_node;
+            if (s_node->stcache != NULL && s_node->stcache_is_valid) return s_node->stcache;
+
+            Type* s_type;
+            if (s_node->stcache == NULL) {
+                s_type = type_create(Type_Kind_Struct, alloc, 0);
+                s_node->stcache = 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);
+                type_register(s_type);
+
+                s_type->Struct.memarr = NULL;
+                bh_table_init(global_heap_allocator, s_type->Struct.members, s_type->Struct.mem_count + 1);
+                bh_arr_new(global_heap_allocator, s_type->Struct.memarr, s_type->Struct.mem_count);
+
+            } else {
+                s_type = s_node->stcache;
+            }
+
+            s_type->Struct.poly_sln = NULL;
+
+            bh_arr_clear(s_type->Struct.memarr);
+            bh_table_clear(s_type->Struct.members);
+
+            s_node->stcache_is_valid = 1;
+
+            b32 is_union = (s_node->flags & Ast_Flag_Struct_Is_Union) != 0;
+            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->stcache_is_valid = 0;
+                    return NULL;
+                }
+
+                mem_alignment = type_alignment_of((*member)->type);
+                if (mem_alignment <= 0) {
+                    onyx_report_error((*member)->token->pos, "Invalid member type: %s. Has alignment %d", type_get_name((*member)->type), mem_alignment); 
+                    return NULL;
+                }
+
+                if (mem_alignment > alignment) alignment = mem_alignment;
+                if (offset % mem_alignment != 0) {
+                    offset += mem_alignment - (offset % mem_alignment);
+                }
+
+                token_toggle_end((*member)->token);
+                StructMember smem = {
+                    .offset = offset,
+                    .type = (*member)->type,
+                    .idx = idx,
+                    .name = bh_strdup(alloc, (*member)->token->text),
+                    .initial_value = &(*member)->initial_value,
+                    .included_through_use = 0,
+                    .used = (((*member)->flags & Ast_Flag_Struct_Mem_Used) != 0),
+                };
+
+                if (bh_table_has(StructMember, s_type->Struct.members, (*member)->token->text)) {
+                    onyx_report_error((*member)->token->pos, "Duplicate struct member, '%s'.", (*member)->token->text);
+                    return NULL;
+                }
+                bh_table_put(StructMember, s_type->Struct.members, (*member)->token->text, smem);
+                token_toggle_end((*member)->token);
+
+                if (smem.used) {
+                    assert((*member)->type->kind == Type_Kind_Struct);
+
+                    bh_arr_each(StructMember*, psmem, (*member)->type->Struct.memarr) {
+                        StructMember new_smem = {
+                            .offset = offset + (*psmem)->offset,
+                            .type   = (*psmem)->type,
+                            .idx    = -1, // Dummy value because I don't think this is needed.
+                            .name   = (*psmem)->name,
+                            .initial_value = (*psmem)->initial_value,
+                            .included_through_use = 1,
+                            .used = 0,
+                        };
+
+                        if (bh_table_has(StructMember, s_type->Struct.members, (*psmem)->name)) {
+                            onyx_report_error((*member)->token->pos, "Duplicate struct member, '%s'.", (*psmem)->name);
+                            return NULL;
+                        }
+                        bh_table_put(StructMember, s_type->Struct.members, (*psmem)->name, new_smem);
+                    }
+                }
+
+                u32 type_size = type_size_of((*member)->type);
+
+                if (!is_union) offset += type_size;
+                if (!is_union) size = offset;
+                else           size = bh_max(size, type_size);
+
+                idx++;
+            }
+
+            // NOTE: Need to do a second pass because the references to the
+            // elements of the table may change if the internal arrays of the
+            // table need to be resized.
+            bh_arr_each(AstStructMember *, member, s_node->members) {
+                token_toggle_end((*member)->token);
+                bh_arr_push(s_type->Struct.memarr, &bh_table_get(StructMember, s_type->Struct.members, (*member)->token->text));
+                token_toggle_end((*member)->token);
+            }
+
+            alignment = bh_max(s_node->min_alignment, alignment);
+            s_type->Struct.alignment = alignment;
+
+            if (size % alignment != 0) {
+                size += alignment - (size % alignment);
+            }
+
+            size = bh_max(s_node->min_size, size);
+            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);
+
+            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->flags & Ast_Flag_Enum_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:
+            //onyx_report_error(type_node->token->pos,
+            //    "This structure is polymorphic, which means you need to provide arguments to it to make it a concrete structure. "
+            //    "This error message is probably in the wrong place, so look through your code for uses of this struct.");
+            return NULL;
+            break;
+
+        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, "Cannot instantiate a concrete type off of a non-polymorphic type.");
+                onyx_report_error(pc_type->callee->token->pos, "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,
+                    }));
+                }
+            }
+
+            AstStructType* concrete = polymorphic_struct_lookup(ps_type, slns, pc_type->token->pos);
+
+            // 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;
+            Type* struct_type = type_build_from_ast(alloc, (AstType *) concrete);
+            struct_type->Struct.constructed_from = (AstType *) ps_type;
+            return struct_type;
+        }
+
+        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;
+        }
+    }
+
+    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 (bh_table_has(u64, type_func_map, name)) {
+        u64 id = bh_table_get(u64, type_func_map, name);
+        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);
+    bh_table_put(u64, 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;
+    }
+
+    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_make_pointer(bh_allocator alloc, Type* to) {
+    if (to == NULL) return NULL;
+
+    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 = 8;
+        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;
+
+    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);
+
+    // :TypeCanBeDuplicated
+    type_register(arr_type);
+    return arr_type;
+}
+
+Type* type_make_slice(bh_allocator alloc, Type* of) {
+    if (of == NULL) return NULL;
+
+    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);
+
+        slice_type->Slice.ptr_to_data = type_make_pointer(alloc, of);
+
+        return slice_type;
+    }
+}
+
+Type* type_make_dynarray(bh_allocator alloc, Type* of) {
+    if (of == NULL) return NULL;
+
+    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);
+
+        dynarr->DynArray.ptr_to_data = type_make_pointer(alloc, of);
+
+        return dynarr;
+    }
+}
+
+Type* type_make_varargs(bh_allocator alloc, Type* of) {
+    if (of == NULL) return NULL;
+    
+    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);
+
+        va_type->VarArgs.ptr_to_data = type_make_pointer(alloc, 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;
+    }
+}
+
+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.ptr_to_data->Pointer.elem));
+        case Type_Kind_VarArgs: return bh_aprintf(global_scratch_allocator, "..%s", type_get_unique_name(type->VarArgs.ptr_to_data->Pointer.elem));
+        case Type_Kind_DynArray: return bh_aprintf(global_scratch_allocator, "[..] %s", type_get_unique_name(type->DynArray.ptr_to_data->Pointer.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);
+        }
+
+
+        default: return "unknown";
+    }
+}
+
+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.ptr_to_data->Pointer.elem));
+        case Type_Kind_VarArgs: return bh_aprintf(global_scratch_allocator, "..%s", type_get_name(type->VarArgs.ptr_to_data->Pointer.elem));
+        case Type_Kind_DynArray: return bh_aprintf(global_scratch_allocator, "[..] %s", type_get_name(type->DynArray.ptr_to_data->Pointer.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);
+        }
+
+        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;
+}
+
+static const StructMember slice_members[] = {
+    { 0, 0, NULL,                         "data",  NULL, 0, 0 },
+    { 8, 1, &basic_types[Basic_Kind_U32], "count", NULL, 0, 0 },
+};
+
+static const StructMember array_members[] = {
+    { 0,  0, NULL,                         "data",      NULL, 0, 0 },
+    { 8,  1, &basic_types[Basic_Kind_U32], "count",     NULL, 0, 0 },
+    { 12, 2, &basic_types[Basic_Kind_U32], "capacity",  NULL, 0, 0 },
+    { 16, 3, NULL,                         "allocator", NULL, 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;
+
+            if (!bh_table_has(StructMember, stype->members, member)) return 0;
+            *smem = bh_table_get(StructMember, stype->members, member);
+            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->Slice.ptr_to_data;
+
+                    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->DynArray.ptr_to_data;
+                    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->Slice.ptr_to_data;
+
+            return 1;
+        }
+
+        case Type_Kind_DynArray: {
+            if (idx > 4) return 0;
+
+            *smem = array_members[idx];
+            if (idx == 0) smem->type = type->DynArray.ptr_to_data;
+            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->Slice.ptr_to_data;
+                two->offset = 0;
+            }
+            if (idx == 1) {
+                two->type = &basic_types[Basic_Kind_U32];
+                two->offset = 8;
+            }
+
+            return 1;
+        }
+        case Type_Kind_DynArray: {
+            if (idx == 0) { 
+                two->type = type->DynArray.ptr_to_data;
+                two->offset = 0;
+            }
+            if (idx == 1) {
+                two->type = &basic_types[Basic_Kind_U32];
+                two->offset = 8;
+            }
+            if (idx == 2) {
+                two->type = &basic_types[Basic_Kind_U32];
+                two->offset = 12;
+            }
+            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 += 16;
+            }
+
+            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;
+        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 == 8) return 1;
+            return -1;
+        }
+        case Type_Kind_DynArray: {
+            if (offset == 0)   return 0;
+            if (offset == 8)   return 1;
+            if (offset == 12)  return 2;
+            if (offset == 16)  return 3;
+            if (offset == 24)  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)) {
+            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_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_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_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;
+    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;
+}
+
+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_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
new file mode 100644 (file)
index 0000000..b98a9e2
--- /dev/null
@@ -0,0 +1,871 @@
+#define BH_DEBUG
+
+#include "utils.h"
+#include "lex.h"
+#include "astnodes.h"
+#include "errors.h"
+#include "parser.h"
+#include "astnodes.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) {
+    if (bh_table_has(Package *, context.packages, package_name)) {
+        return bh_table_get(Package *, context.packages, package_name);
+    } else {
+        return NULL;
+    }
+}
+
+Package* package_lookup_or_create(char* package_name, Scope* parent_scope, bh_allocator alloc) {
+    if (bh_table_has(Package *, context.packages, package_name)) {
+        return bh_table_get(Package *, context.packages, package_name);
+
+    } 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->scope = scope_create(alloc, parent_scope, (OnyxFilePos) { 0 });
+        package->private_scope = scope_create(alloc, package->scope, (OnyxFilePos) { 0 });
+        package->use_package_entities = NULL;
+
+        bh_table_put(Package *, context.packages, pac_name, package);
+
+        return package;
+    }
+}
+
+void package_track_use_package(Package* package, Entity* 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->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->symbols = NULL;
+    bh_table_init(global_heap_allocator, scope->symbols, 64);
+
+    return scope;
+}
+
+void scope_include(Scope* target, Scope* source, OnyxFilePos pos) {
+    bh_table_each_start(AstNode *, source->symbols);
+        symbol_raw_introduce(target, (char *) key, pos, value);
+    bh_table_each_end;
+}
+
+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, "_")) {
+        if (bh_table_has(AstNode *, scope->symbols, name)) {
+            if (bh_table_get(AstNode *, scope->symbols, name) != symbol) {
+                onyx_report_error(pos, "Redeclaration of symbol '%s'.", name);
+                return 0;
+            }
+            return 1;
+        }
+    }
+
+    bh_table_put(AstNode *, scope->symbols, name, symbol);
+    return 1;
+}
+
+void symbol_builtin_introduce(Scope* scope, char* sym, AstNode *node) {
+    bh_table_put(AstNode *, scope->symbols, sym, node);
+}
+
+void symbol_subpackage_introduce(Scope* scope, char* sym, AstPackage* package) {
+    if (bh_table_has(AstNode *, scope->symbols, sym)) {
+        AstNode* maybe_package = bh_table_get(AstNode *, scope->symbols, sym);
+        
+        // CLEANUP: Make this assertion an actual error message.
+        assert(maybe_package->kind == Ast_Kind_Package);
+    } else {
+        bh_table_put(AstNode *, scope->symbols, sym, (AstNode *) package);
+    }
+}
+
+AstNode* symbol_raw_resolve(Scope* start_scope, char* sym) {
+    AstNode* res = NULL;
+    Scope* scope = start_scope;
+
+    while (res == NULL && scope != NULL) {
+        if (bh_table_has(AstNode *, scope->symbols, sym)) {
+            res = bh_table_get(AstNode *, scope->symbols, sym);
+        } else {
+            scope = scope->parent;
+        }
+    }
+
+    return res;
+}
+
+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_Enum_Type: {
+            AstEnumType* etype = (AstEnumType *) node;
+            return symbol_raw_resolve(etype->scope, symbol);
+        }
+
+        case Ast_Kind_Struct_Type: {
+            AstStructType* stype = (AstStructType *) node;
+            AstNode* result = symbol_raw_resolve(stype->scope, symbol);
+
+            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);
+        }
+    }
+
+    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;
+}
+
+void scope_clear(Scope* scope) {
+    bh_table_clear(scope->symbols);
+}
+
+// Polymorphic procedures are in their own file to clean up this file.
+#include "polymorph.c"
+
+//
+// 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, b32* should_yield) {
+    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 *) entry->key;
+        arguments_copy(&args, param_args);
+
+        AstFunction* overload = NULL;
+        switch (node->kind) {
+            case Ast_Kind_Function:         overload = (AstFunction *) node; break;
+            case Ast_Kind_Macro:            overload = macro_resolve_header((AstMacro *) node, param_args, NULL); break;
+            case Ast_Kind_Polymorphic_Proc: overload = polymorphic_proc_build_only_header((AstPolyProc *) node, PPLM_By_Arguments, param_args); break;
+        }
+
+        // NOTE: Overload is not something that is known to be overloadable.
+        if (overload == NULL) continue;
+        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.
+            if (should_yield) *should_yield = 1;
+
+            // 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 NULL;
+        }
+        assert(overload->type->kind == Type_Kind_Function);
+
+        // NOTE: If the arguments cannot be placed successfully in the parameters list
+        if (!fill_in_arguments(&args, (AstNode *) overload, NULL)) continue;
+
+        TypeFunction* ol_type = &overload->type->Function;
+        if (bh_arr_length(args.values) < (i32) ol_type->needed_param_count) continue;
+
+        b32 all_arguments_work = 1;
+        fori (i, 0, bh_arr_length(args.values)) {
+            if (i >= ol_type->param_count) {
+                all_arguments_work = 0;
+                break;
+            }
+
+            Type* type_to_match = ol_type->params[i];
+            AstTyped** value    = &args.values[i];
+
+            if (type_to_match->kind == Type_Kind_VarArgs) type_to_match = type_to_match->VarArgs.ptr_to_data->Pointer.elem;
+            if ((*value)->kind == Ast_Kind_Argument) {
+                // :ArgumentResolvingIsComplicated
+                if (((AstArgument *) (*value))->is_baked) continue;
+                value = &((AstArgument *) *value)->value;
+            }
+
+            if (!type_check_or_auto_cast(value, type_to_match)) {
+                all_arguments_work = 0;
+                break;
+            }
+        }
+
+        if (all_arguments_work) {
+            matched_overload = node;
+            break;
+        }
+    }
+
+    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;
+
+        if (type_check_or_auto_cast(&node, type)) {
+            matched_overload = node;
+            break;
+        }
+    }
+    
+    bh_imap_free(&all_overloads);
+    return matched_overload;
+}
+
+void report_unable_to_match_overload(AstCall* call) {
+    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, "Unable to match overloaded function with provided argument types: (%s)", arg_str);
+
+    bh_free(global_scratch_allocator, arg_str);
+}
+
+
+//
+// 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.
+    fori (i, 0, bh_arr_length(call->args.values)) {
+        symbol_introduce(argument_scope,
+            template->params[i].local->token,
+            (AstNode *) ((AstArgument *) call->args.values[i])->value);
+    }
+
+    if (template->flags & Ast_Flag_From_Polymorphism) {
+        // SLOW DUMB HACKY WAY TO DO THIS!!!!! FIX IT!!!!!
+
+        AstPolyProc* pp = (AstPolyProc *) macro->body;
+        bh_table_each_start(AstSolidifiedFunction, pp->concrete_funcs);
+
+            if (value.func == template) {
+                scope_include(argument_scope, value.poly_scope, call->token->pos);
+                break;
+            }
+
+        bh_table_each_end;
+    }
+
+    *(AstNode **) pcall = subst;
+    return;
+}
+
+AstFunction* macro_resolve_header(AstMacro* macro, Arguments* args, OnyxToken* callsite) {
+    switch (macro->body->kind) {
+        case Ast_Kind_Function: return (AstFunction *) macro->body;
+
+        case Ast_Kind_Polymorphic_Proc: {
+            AstPolyProc* pp = (AstPolyProc *) macro->body;
+            ensure_polyproc_cache_is_created(pp);
+
+            char* err_msg=NULL;
+            bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, PPLM_By_Arguments, args, &err_msg);
+
+            if (slns == NULL) {
+                if (flag_to_yield) {
+                    flag_to_yield = 0;
+                    return (AstFunction *) &node_that_signals_a_yield;
+                }
+
+                if (callsite) onyx_report_error(callsite->pos, err_msg);
+
+                return NULL;
+            }
+
+            // CLEANUP Copy'n'pasted from polymorphic_proc_build_only_header
+            AstSolidifiedFunction solidified_func;
+
+            char* unique_key = build_poly_slns_unique_key(slns);
+            if (bh_table_has(AstSolidifiedFunction, pp->concrete_funcs, unique_key)) {
+                solidified_func = bh_table_get(AstSolidifiedFunction, pp->concrete_funcs, unique_key);
+
+            } 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, callsite, 1);
+            }
+
+            if (solidified_func.header_complete) return solidified_func.func;
+
+            Entity func_header_entity = {
+                .state = Entity_State_Resolve_Symbols,
+                .type = Entity_Type_Function_Header,
+                .function = solidified_func.func,
+                .package = NULL,
+                .scope = solidified_func.poly_scope,
+            };
+
+            b32 successful = entity_bring_to_state(&func_header_entity, Entity_State_Code_Gen);
+            if (onyx_has_errors()) return NULL;
+
+            solidified_func.header_complete = successful;
+
+            bh_table_put(AstSolidifiedFunction, pp->concrete_funcs, unique_key, solidified_func);
+            if (!successful) return (AstFunction *) &node_that_signals_a_yield;
+
+            return solidified_func.func;
+        }
+
+        default: assert(("Bad macro body type.", 0));
+    }
+}
+
+b32 entity_bring_to_state(Entity* ent, EntityState state) {
+    EntityState last_state = ent->state;
+
+    while (ent->state != state) {
+        switch (ent->state) {
+            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;
+
+            default: return 0;
+        }
+
+        if (ent->state == last_state) return 0;
+        last_state = ent->state;
+
+        if (onyx_has_errors()) return 0;
+    }
+
+    return 1;
+}
+
+
+//
+// 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;
+}
+
+// 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) {
+
+    { // 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)) {
+                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) {
+            *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) {
+        *err_msg = bh_aprintf(global_scratch_allocator, "Too many values provided. Expected at most %d.", maximum_arguments);
+        success = 0;
+    }
+
+    return 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;
+}
+
+char* lookup_included_file(char* filename, char* relative_to, b32 add_onyx_suffix, b32 search_included_folders) {
+    assert(relative_to != NULL);
+
+    static char path[256];
+    fori (i, 0, 256) path[i] = 0;
+
+    static char fn[128];
+    fori (i, 0, 128) fn[i] = 0;
+
+    if (!bh_str_ends_with(filename, ".onyx") && add_onyx_suffix) {
+        bh_snprintf(fn, 128, "%s.onyx", filename);
+    } else {
+        bh_snprintf(fn, 128, "%s", filename);
+    }
+
+#if defined(_BH_LINUX)
+    #define DIR_SEPARATOR '/'
+#elif defined(_BH_WINDOWS)
+    #define DIR_SEPARATOR '\\'
+#endif
+
+    fori (i, 0, 128) 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, 256, "%s%c%s", relative_to, DIR_SEPARATOR, fn + 2);
+        else
+            bh_snprintf(path, 256, "%s%s", relative_to, fn + 2);
+
+        if (bh_file_exists(path)) return bh_path_get_full_name(path, global_scratch_allocator);
+
+        return fn;
+    }
+
+    if (search_included_folders) {
+        bh_arr_each(const char *, folder, context.options->included_folders) {
+            if ((*folder)[strlen(*folder) - 1] != DIR_SEPARATOR)
+                bh_snprintf(path, 256, "%s%c%s", *folder, DIR_SEPARATOR, fn);
+            else
+                bh_snprintf(path, 256, "%s%s", *folder, fn);
+
+            if (bh_file_exists(path)) return bh_path_get_full_name(path, global_scratch_allocator);
+        }
+    }
+
+    return fn;
+
+#undef DIR_SEPARATOR
+}
+
diff --git a/src/wasm.c b/src/wasm.c
new file mode 100644 (file)
index 0000000..144deab
--- /dev/null
@@ -0,0 +1,3672 @@
+//
+// 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.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) {
+        return WASM_TYPE_VOID;
+    }
+
+    if (type->kind == Type_Kind_Slice) {
+        return WASM_TYPE_VOID;
+    }
+
+    if (type->kind == Type_Kind_Enum) {
+        return onyx_type_to_wasm_type(type->Enum.backing);
+    }
+
+    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_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;
+}
+
+
+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,
+};
+
+
+#define WI(instr) bh_arr_push(code, ((WasmInstruction){ instr, 0x00 }))
+#define WID(instr, data) bh_arr_push(code, ((WasmInstruction){ instr, data }))
+#define WIL(instr, data) bh_arr_push(code, ((WasmInstruction){ instr, { .l = data } }))
+#define WIP(instr, data) bh_arr_push(code, ((WasmInstruction){ instr, { .p = data } }))
+#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(type1, type2) { \
+    u64 t0 = local_raw_allocate(mod->local_alloc, type1); \
+    u64 t1 = local_raw_allocate(mod->local_alloc, type2); \
+                                                          \
+    WIL(WI_LOCAL_SET, t0);                                \
+    WIL(WI_LOCAL_SET, t1);                                \
+    WIL(WI_LOCAL_GET, t0);                                \
+    WIL(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 }))
+
+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(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(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);
+EMIT_FUNC_NO_ARGS(leave_structured_block);
+
+static void emit_raw_data(OnyxWasmModule* mod, ptr data, AstTyped* node);
+static b32 emit_raw_data_(OnyxWasmModule* mod, ptr data, AstTyped* node);
+
+#include "wasm_intrinsics.c"
+#include "wasm_type_table.c"
+
+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);
+    }
+
+    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);
+
+    *pcode = code;
+}
+
+EMIT_FUNC(enter_structured_block, StructuredBlockType sbt) {
+    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(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(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(WI_JUMP, labelidx);
+    } else {
+        onyx_report_error(jump->token->pos, "Invalid structured jump.");
+    }
+
+    *pcode = code;
+}
+
+EMIT_FUNC(statement, AstNode* stmt) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    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_Insert: break;
+
+        default:                  emit_expression(mod, &code, (AstTyped *) stmt); break;
+    }
+
+    *pcode = code;
+}
+
+EMIT_FUNC(local_allocation, AstTyped* stmt) {
+    bh_imap_put(&mod->local_map, (u64) stmt, local_allocate(mod->local_alloc, stmt));
+
+    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(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(WI_LOCAL_SET, localidx + i);
+
+            } else {
+                WIL(WI_LOCAL_SET, localidx);
+            }
+
+            *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(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(WI_LOCAL_SET, lptr_local);
+
+        AstArrayLiteral* al = (AstArrayLiteral *) right;
+        fori (i, 0, elem_count) {
+            WIL(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(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(WI_LOCAL_SET, expr_tmp);
+        u64 offset = 0;
+        emit_location_return_offset(mod, &code, lval, &offset);
+        WIL(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_Array) {
+        emit_array_store(mod, pcode, type, offset);
+        return;
+    }
+
+    if (type->kind == Type_Kind_Enum)     type = type->Enum.backing;
+    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(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(WI_I32_STORE_8,  ((WasmInstructionData) { alignment, offset }));
+        else if (store_size == 2)   WID(WI_I32_STORE_16, ((WasmInstructionData) { alignment, offset }));
+        else if (store_size == 4)   WID(WI_I32_STORE,    ((WasmInstructionData) { alignment, offset }));
+        else if (store_size == 8)   WID(WI_I64_STORE,    ((WasmInstructionData) { alignment, offset }));
+    } else if (is_basic && (type->Basic.flags & Basic_Flag_Float)) {
+        if      (store_size == 4)   WID(WI_F32_STORE, ((WasmInstructionData) { alignment, offset }));
+        else if (store_size == 8)   WID(WI_F64_STORE, ((WasmInstructionData) { alignment, offset }));
+    } else if (is_basic && (type->Basic.flags & Basic_Flag_SIMD)) {
+        WID(WI_V128_STORE, ((WasmInstructionData) { alignment, offset }));
+    } else {
+        onyx_report_error((OnyxFilePos) { 0 },
+            "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_Array) {
+        if (offset != 0) {
+            WID(WI_PTR_CONST, offset);
+            WI(WI_PTR_ADD);
+        }
+
+        *pcode = code;
+        return;
+    }
+
+    if (type->kind == Type_Kind_Enum)     type = type->Enum.backing;
+    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(instr, ((WasmInstructionData) { alignment, offset }));
+
+    if (instr == WI_NOP) {
+        onyx_report_error((OnyxFilePos) { 0 },
+            "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 (if_node->true_stmt) emit_block(mod, &code, if_node->true_stmt, 0);
+
+    if (if_node->false_stmt) {
+        WI(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);
+        emit_enter_structured_block(mod, &code, SBT_Continue_Loop);
+
+        emit_expression(mod, &code, while_node->cond);
+        WI(WI_I32_EQZ);
+        WID(WI_COND_JUMP, 0x01);
+
+        emit_block(mod, &code, while_node->true_stmt, 0);
+
+        if (bh_arr_last(code).type != WI_JUMP)
+            WID(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);
+        emit_enter_structured_block(mod, &code, SBT_Continue_Loop);
+
+        emit_block(mod, &code, while_node->true_stmt, 0);
+
+        emit_expression(mod, &code, while_node->cond);
+        WID(WI_COND_JUMP, 0x00);
+
+        emit_leave_structured_block(mod, &code);
+        WI(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
+
+    AstLocal* var = for_node->var;
+    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(WI_LOCAL_SET, step_local);
+    WIL(WI_LOCAL_SET, high_local);
+    WIL(WI_LOCAL_TEE, low_local);
+    WIL(WI_LOCAL_SET, iter_local);
+
+    emit_enter_structured_block(mod, &code, SBT_Breakable_Block);
+    emit_enter_structured_block(mod, &code, SBT_Basic_Loop);
+    emit_enter_structured_block(mod, &code, SBT_Continue_Block);
+
+    WIL(WI_LOCAL_GET, iter_local);
+    WIL(WI_LOCAL_GET, high_local);
+    WI(WI_I32_GE_S);
+    WID(WI_COND_JUMP, 0x02);
+
+    emit_block(mod, &code, for_node->stmt, 0);
+
+    emit_leave_structured_block(mod, &code);
+
+    WIL(WI_LOCAL_GET, iter_local);
+    WIL(WI_LOCAL_GET, step_local);
+    WI(WI_I32_ADD);
+    WIL(WI_LOCAL_SET, iter_local);
+
+    if (bh_arr_last(code).type != WI_JUMP)
+        WID(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(WI_LOCAL_TEE, ptr_local);
+    WIL(WI_PTR_CONST, for_node->iter->type->Array.count * elem_size);
+    WI(WI_PTR_ADD);
+    WIL(WI_LOCAL_SET, end_ptr_local);
+
+    emit_enter_structured_block(mod, &code, SBT_Breakable_Block);
+    emit_enter_structured_block(mod, &code, SBT_Basic_Loop);
+    emit_enter_structured_block(mod, &code, SBT_Continue_Block);
+
+    WIL(WI_LOCAL_GET, ptr_local);
+    WIL(WI_LOCAL_GET, end_ptr_local);
+    WI(WI_PTR_GE);
+    WID(WI_COND_JUMP, 0x02);
+
+    if (!for_node->by_pointer) {
+        if (!it_is_local) emit_local_location(mod, &code, var, &offset);
+
+        WIL(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(WI_LOCAL_SET, iter_local);
+    }
+
+    emit_block(mod, &code, for_node->stmt, 0);
+
+    emit_leave_structured_block(mod, &code);
+
+    WIL(WI_LOCAL_GET, ptr_local);
+    WIL(WI_PTR_CONST, elem_size);
+    WI(WI_PTR_ADD);
+    WIL(WI_LOCAL_SET, ptr_local);
+
+    if (bh_arr_last(code).type != WI_JUMP)
+        WID(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(WI_LOCAL_SET, end_ptr_local);
+    WIL(WI_LOCAL_TEE, ptr_local);
+    WIL(WI_LOCAL_GET, end_ptr_local);
+    if (elem_size != 1) {
+        WID(WI_PTR_CONST, elem_size);
+        WI(WI_PTR_MUL);
+    }
+    WI(WI_PTR_ADD);
+    WIL(WI_LOCAL_SET, end_ptr_local);
+
+    emit_enter_structured_block(mod, &code, SBT_Breakable_Block);
+    emit_enter_structured_block(mod, &code, SBT_Basic_Loop);
+    emit_enter_structured_block(mod, &code, SBT_Continue_Block);
+
+    WIL(WI_LOCAL_GET, ptr_local);
+    WIL(WI_LOCAL_GET, end_ptr_local);
+    WI(WI_PTR_GE);
+    WID(WI_COND_JUMP, 0x02);
+
+    if (!for_node->by_pointer) {
+        if (!it_is_local) emit_local_location(mod, &code, var, &offset);
+
+        WIL(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(WI_LOCAL_SET, iter_local);
+    }
+
+    emit_block(mod, &code, for_node->stmt, 0);
+
+    emit_leave_structured_block(mod, &code);
+
+    WIL(WI_LOCAL_GET, ptr_local);
+    WIL(WI_PTR_CONST, elem_size);
+    WI(WI_PTR_ADD);
+    WIL(WI_LOCAL_SET, ptr_local);
+
+    if (bh_arr_last(code).type != WI_JUMP)
+        WID(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_done_bool  = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
+    WIL(WI_LOCAL_SET, iterator_close_func);
+    WIL(WI_LOCAL_SET, iterator_next_func);
+    WIL(WI_LOCAL_SET, iterator_data_ptr);
+
+    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);
+
+    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);
+    emit_enter_structured_block(mod, &code, SBT_Continue_Loop);
+
+    if (!it_is_local) emit_local_location(mod, &code, var, &offset);
+
+    {
+        WIL(WI_LOCAL_GET, iterator_data_ptr);
+        WIL(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(WI_GLOBAL_GET, stack_top_idx);
+        WID(WI_PTR_CONST, reserve_size);
+        WI(WI_PTR_ADD);
+        WID(WI_GLOBAL_SET, stack_top_idx);
+
+        i32 type_idx = generate_type_idx(mod, next_func_type.type);
+        WID(WI_CALL_INDIRECT, ((WasmInstructionData) { type_idx, 0x00 }));
+
+        WID(WI_GLOBAL_GET, stack_top_idx);
+        WID(WI_PTR_CONST, reserve_size);
+        WI(WI_PTR_SUB);
+        WID(WI_GLOBAL_SET, stack_top_idx);
+
+        WID(WI_GLOBAL_GET, stack_top_idx);
+        emit_load_instruction(mod, &code, return_type, reserve_size - return_size);
+    }
+
+    WIL(WI_LOCAL_SET, iterator_done_bool);
+
+    if (!it_is_local) emit_store_instruction(mod, &code, var->type, offset);
+    else              WIL(WI_LOCAL_SET, iter_local);
+
+    WIL(WI_LOCAL_GET, iterator_done_bool);
+    WI(WI_I32_EQZ);
+    WID(WI_COND_JUMP, 0x01);
+
+    emit_block(mod, &code, for_node->stmt, 0);
+    WID(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);
+
+    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);
+
+    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(WI_DROP); WI(WI_DROP); WI(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, "Invalid for loop type. You should probably not be seeing this...");
+    }
+
+    local_free(mod->local_alloc, (AstTyped *) var);
+
+    *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);
+
+    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);
+
+        bh_imap_put(&block_map, (u64) sc->block, block_num);
+        block_num++;
+    }
+
+    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);
+    }
+
+    // CLEANUP: 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(WI_BLOCK_START, 0x40);
+    emit_expression(mod, &code, switch_node->expr);
+    if (switch_node->min_case != 0) {
+        WID(WI_I32_CONST, switch_node->min_case);
+        WI(WI_I32_SUB);
+    }
+    WIP(WI_JUMP_TABLE, bt);
+    WI(WI_BLOCK_END);
+
+    bh_arr_each(AstSwitchCase, sc, switch_node->cases) {
+        if (bh_imap_get(&block_map, (u64) sc->block) == 0xdeadbeef) continue;
+
+        u64 bn = bh_imap_get(&block_map, (u64) sc->block);
+
+        emit_block(mod, &code, sc->block, 0);
+
+        if (bh_arr_last(code).type != WI_JUMP)
+            WID(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),
+        .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),
+        .instructions = deferred_code,
+        .instruction_count = code_count,
+    }));
+}
+
+EMIT_FUNC(deferred_stmt, DeferredStmt deferred_stmt) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    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) {
+                bh_arr_push(code, 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);
+    }
+}
+
+// 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_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(WI_I32_CONST, 0x00);
+                emit_expression(mod, &code, unop->expr);
+                WI(WI_I32_SUB);
+
+            }
+            else if (type->kind == Basic_Kind_I64) {
+                WID(WI_I64_CONST, 0x00);
+                emit_expression(mod, &code, unop->expr);
+                WI(WI_I64_SUB);
+
+            }
+            else {
+                emit_expression(mod, &code, unop->expr);
+
+                if (type->kind == Basic_Kind_F32) WI(WI_F32_NEG);
+                if (type->kind == Basic_Kind_F64) WI(WI_F64_NEG);
+            }
+
+            break;
+        }
+
+        case Unary_Op_Not:
+            emit_expression(mod, &code, unop->expr);
+
+            WI(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(WI_I32_CONST, 0xff);
+                WI(WI_I32_XOR);
+            }
+            else if (type->kind == Basic_Kind_I16 || type->kind == Basic_Kind_U16) {
+                WID(WI_I32_CONST, 0xffff);
+                WI(WI_I32_XOR);
+            }
+            else if (type->kind == Basic_Kind_I32 || type->kind == Basic_Kind_U32) {
+                WID(WI_I32_CONST, 0xffffffff);
+                WI(WI_I32_XOR);
+            }
+            else if (type->kind == Basic_Kind_I64 || type->kind == Basic_Kind_U64) {
+                WIL(WI_I64_CONST, 0xffffffffffffffff);
+                WI(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);
+
+    // 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(WI_GLOBAL_GET, stack_top_idx);
+    WIL(WI_LOCAL_TEE, stack_top_store_local);
+    WID(WI_PTR_CONST, 0);                           // This will be filled in later.
+    WI(WI_PTR_ADD);
+    WID(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 (place_on_stack) WIL(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(WI_LOCAL_GET, stack_top_store_local);
+                WID(WI_PTR_CONST, reserve_size);
+                WI(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;
+            }
+
+            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(WI_LOCAL_GET, stack_top_store_local);
+                WIL(WI_LOCAL_GET, stack_top_store_local);
+                WID(WI_PTR_CONST, vararg_any_offsets[i]);
+                WI(WI_PTR_ADD);
+                emit_store_instruction(mod, &code, &basic_types[Basic_Kind_Rawptr], vararg_offset + i * any_size);
+
+                WIL(WI_LOCAL_GET, stack_top_store_local);
+                WID(WI_I32_CONST, vararg_any_types[i]);
+                emit_store_instruction(mod, &code, &basic_types[Basic_Kind_Type_Index], vararg_offset + i * any_size + 8);
+
+                reserve_size += any_size;
+            }
+
+            // fallthrough
+        }
+
+        case VA_Kind_Typed: {
+            WIL(WI_LOCAL_GET, stack_top_store_local);
+            if (vararg_offset > 0) {
+                WID(WI_PTR_CONST, vararg_offset);
+                WI(WI_PTR_ADD);
+            }
+            WID(WI_I32_CONST, vararg_count);
+            break;
+        }
+
+        case VA_Kind_Untyped: {
+            WIL(WI_LOCAL_GET, stack_top_store_local);
+            WIL(WI_LOCAL_GET, stack_top_store_local);
+            if (vararg_offset > 0) {
+                WID(WI_PTR_CONST, vararg_offset);
+                WI(WI_PTR_ADD);
+            }
+            emit_store_instruction(mod, &code, &basic_types[Basic_Kind_Rawptr], reserve_size);
+
+            // NOTE: There will be 4 uninitialized bytes here, because pointers are only 4 bytes in WASM.
+
+            WIL(WI_LOCAL_GET, stack_top_store_local);
+            WID(WI_I32_CONST, vararg_count);
+            emit_store_instruction(mod, &code, &basic_types[Basic_Kind_I32], reserve_size + 8);
+
+            WIL(WI_LOCAL_GET, stack_top_store_local);
+            if (reserve_size > 0) {
+                WID(WI_PTR_CONST, reserve_size);
+                WI(WI_PTR_ADD);
+            }
+
+            reserve_size += 12;
+            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);
+        bh_arr_push(code, ((WasmInstruction){ WI_CALL, func_idx }));
+
+    } else {
+        emit_expression(mod, &code, call->callee);
+
+        i32 type_idx = generate_type_idx(mod, call->callee->type);
+        WID(WI_CALL_INDIRECT, ((WasmInstructionData) { type_idx, 0x00 }));
+    }
+
+    if (reserve_size > 0) {
+        WIL(WI_LOCAL_GET,  stack_top_store_local);
+        WID(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(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, \
+                        "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(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, "SIMD lane instructions expect a compile time lane number."); \
+        *pcode = code; \
+        return; \
+    } \
+    WID(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, "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(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(WI_MEMORY_SIZE, 0x00); break;
+        case ONYX_INTRINSIC_MEMORY_GROW:  WID(WI_MEMORY_GROW, 0x00); break;
+        case ONYX_INTRINSIC_MEMORY_COPY:
+            if (context.options->use_post_mvp_features) {
+                WIL(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(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_ZERO_VALUE: {
+            // NOTE: This probably will not have to make an allocation.
+            Type* zero_type = type_build_from_ast(context.ast_alloc, (AstType *) ((AstArgument *) call->original_args.values[0])->value);
+            emit_zero_value_for_type(mod, &code, zero_type, call->token);
+            break;
+        }
+
+        case ONYX_INTRINSIC_I32_CLZ:      WI(WI_I32_CLZ); break;
+        case ONYX_INTRINSIC_I32_CTZ:      WI(WI_I32_CTZ); break;
+        case ONYX_INTRINSIC_I32_POPCNT:   WI(WI_I32_POPCNT); break;
+        case ONYX_INTRINSIC_I32_AND:      WI(WI_I32_AND); break;
+        case ONYX_INTRINSIC_I32_OR:       WI(WI_I32_OR); break;
+        case ONYX_INTRINSIC_I32_XOR:      WI(WI_I32_XOR); break;
+        case ONYX_INTRINSIC_I32_SHL:      WI(WI_I32_SHL); break;
+        case ONYX_INTRINSIC_I32_SLR:      WI(WI_I32_SHR_U); break;
+        case ONYX_INTRINSIC_I32_SAR:      WI(WI_I32_SHR_S); break;
+        case ONYX_INTRINSIC_I32_ROTL:     WI(WI_I32_ROTL); break;
+        case ONYX_INTRINSIC_I32_ROTR:     WI(WI_I32_ROTR); break;
+
+        case ONYX_INTRINSIC_I64_CLZ:      WI(WI_I64_CLZ); break;
+        case ONYX_INTRINSIC_I64_CTZ:      WI(WI_I64_CTZ); break;
+        case ONYX_INTRINSIC_I64_POPCNT:   WI(WI_I64_POPCNT); break;
+        case ONYX_INTRINSIC_I64_AND:      WI(WI_I64_AND); break;
+        case ONYX_INTRINSIC_I64_OR:       WI(WI_I64_OR); break;
+        case ONYX_INTRINSIC_I64_XOR:      WI(WI_I64_XOR); break;
+        case ONYX_INTRINSIC_I64_SHL:      WI(WI_I64_SHL); break;
+        case ONYX_INTRINSIC_I64_SLR:      WI(WI_I64_SHR_U); break;
+        case ONYX_INTRINSIC_I64_SAR:      WI(WI_I64_SHR_S); break;
+        case ONYX_INTRINSIC_I64_ROTL:     WI(WI_I64_ROTL); break;
+        case ONYX_INTRINSIC_I64_ROTR:     WI(WI_I64_ROTR); break;
+
+        case ONYX_INTRINSIC_F32_ABS:      WI(WI_F32_ABS); break;
+        case ONYX_INTRINSIC_F32_CEIL:     WI(WI_F32_CEIL); break;
+        case ONYX_INTRINSIC_F32_FLOOR:    WI(WI_F32_FLOOR); break;
+        case ONYX_INTRINSIC_F32_TRUNC:    WI(WI_F32_TRUNC); break;
+        case ONYX_INTRINSIC_F32_NEAREST:  WI(WI_F32_NEAREST); break;
+        case ONYX_INTRINSIC_F32_SQRT:     WI(WI_F32_SQRT); break;
+        case ONYX_INTRINSIC_F32_MIN:      WI(WI_F32_MIN); break;
+        case ONYX_INTRINSIC_F32_MAX:      WI(WI_F32_MAX); break;
+        case ONYX_INTRINSIC_F32_COPYSIGN: WI(WI_F32_COPYSIGN); break;
+
+        case ONYX_INTRINSIC_F64_ABS:      WI(WI_F64_ABS); break;
+        case ONYX_INTRINSIC_F64_CEIL:     WI(WI_F64_CEIL); break;
+        case ONYX_INTRINSIC_F64_FLOOR:    WI(WI_F64_FLOOR); break;
+        case ONYX_INTRINSIC_F64_TRUNC:    WI(WI_F64_TRUNC); break;
+        case ONYX_INTRINSIC_F64_NEAREST:  WI(WI_F64_NEAREST); break;
+        case ONYX_INTRINSIC_F64_SQRT:     WI(WI_F64_SQRT); break;
+        case ONYX_INTRINSIC_F64_MIN:      WI(WI_F64_MIN); break;
+        case ONYX_INTRINSIC_F64_MAX:      WI(WI_F64_MAX); break;
+        case ONYX_INTRINSIC_F64_COPYSIGN: WI(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,
+                            "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(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,
+                            "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(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,
+                            "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(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(WI_I8X16_SWIZZLE); break;
+        case ONYX_INTRINSIC_I8X16_SPLAT:   WI(WI_I8X16_SPLAT); break;
+        case ONYX_INTRINSIC_I16X8_SPLAT:   WI(WI_I16X8_SPLAT); break;
+        case ONYX_INTRINSIC_I32X4_SPLAT:   WI(WI_I32X4_SPLAT); break;
+        case ONYX_INTRINSIC_I64X2_SPLAT:   WI(WI_I64X2_SPLAT); break;
+        case ONYX_INTRINSIC_F32X4_SPLAT:   WI(WI_F32X4_SPLAT); break;
+        case ONYX_INTRINSIC_F64X2_SPLAT:   WI(WI_F64X2_SPLAT); break;
+
+        case ONYX_INTRINSIC_I8X16_EQ:   WI(WI_I8X16_EQ); break;
+        case ONYX_INTRINSIC_I8X16_NEQ:  WI(WI_I8X16_NEQ); break;
+        case ONYX_INTRINSIC_I8X16_LT_S: WI(WI_I8X16_LT_S); break;
+        case ONYX_INTRINSIC_I8X16_LT_U: WI(WI_I8X16_LT_U); break;
+        case ONYX_INTRINSIC_I8X16_GT_S: WI(WI_I8X16_GT_S); break;
+        case ONYX_INTRINSIC_I8X16_GT_U: WI(WI_I8X16_GT_U); break;
+        case ONYX_INTRINSIC_I8X16_LE_S: WI(WI_I8X16_LE_S); break;
+        case ONYX_INTRINSIC_I8X16_LE_U: WI(WI_I8X16_LE_U); break;
+        case ONYX_INTRINSIC_I8X16_GE_S: WI(WI_I8X16_GE_S); break;
+        case ONYX_INTRINSIC_I8X16_GE_U: WI(WI_I8X16_GE_U); break;
+
+        case ONYX_INTRINSIC_I16X8_EQ:   WI(WI_I16X8_EQ); break;
+        case ONYX_INTRINSIC_I16X8_NEQ:  WI(WI_I16X8_NEQ); break;
+        case ONYX_INTRINSIC_I16X8_LT_S: WI(WI_I16X8_LT_S); break;
+        case ONYX_INTRINSIC_I16X8_LT_U: WI(WI_I16X8_LT_U); break;
+        case ONYX_INTRINSIC_I16X8_GT_S: WI(WI_I16X8_GT_S); break;
+        case ONYX_INTRINSIC_I16X8_GT_U: WI(WI_I16X8_GT_U); break;
+        case ONYX_INTRINSIC_I16X8_LE_S: WI(WI_I16X8_LE_S); break;
+        case ONYX_INTRINSIC_I16X8_LE_U: WI(WI_I16X8_LE_U); break;
+        case ONYX_INTRINSIC_I16X8_GE_S: WI(WI_I16X8_GE_S); break;
+        case ONYX_INTRINSIC_I16X8_GE_U: WI(WI_I16X8_GE_U); break;
+
+        case ONYX_INTRINSIC_I32X4_EQ:   WI(WI_I32X4_EQ); break;
+        case ONYX_INTRINSIC_I32X4_NEQ:  WI(WI_I32X4_NEQ); break;
+        case ONYX_INTRINSIC_I32X4_LT_S: WI(WI_I32X4_LT_S); break;
+        case ONYX_INTRINSIC_I32X4_LT_U: WI(WI_I32X4_LT_U); break;
+        case ONYX_INTRINSIC_I32X4_GT_S: WI(WI_I32X4_GT_S); break;
+        case ONYX_INTRINSIC_I32X4_GT_U: WI(WI_I32X4_GT_U); break;
+        case ONYX_INTRINSIC_I32X4_LE_S: WI(WI_I32X4_LE_S); break;
+        case ONYX_INTRINSIC_I32X4_LE_U: WI(WI_I32X4_LE_U); break;
+        case ONYX_INTRINSIC_I32X4_GE_S: WI(WI_I32X4_GE_S); break;
+        case ONYX_INTRINSIC_I32X4_GE_U: WI(WI_I32X4_GE_U); break;
+
+        case ONYX_INTRINSIC_F32X4_EQ:  WI(WI_F32X4_EQ); break;
+        case ONYX_INTRINSIC_F32X4_NEQ: WI(WI_F32X4_NEQ); break;
+        case ONYX_INTRINSIC_F32X4_LT:  WI(WI_F32X4_LT); break;
+        case ONYX_INTRINSIC_F32X4_GT:  WI(WI_F32X4_GT); break;
+        case ONYX_INTRINSIC_F32X4_LE:  WI(WI_F32X4_LE); break;
+        case ONYX_INTRINSIC_F32X4_GE:  WI(WI_F32X4_GE); break;
+
+        case ONYX_INTRINSIC_F64X2_EQ:  WI(WI_F64X2_EQ); break;
+        case ONYX_INTRINSIC_F64X2_NEQ: WI(WI_F64X2_NEQ); break;
+        case ONYX_INTRINSIC_F64X2_LT:  WI(WI_F64X2_LT); break;
+        case ONYX_INTRINSIC_F64X2_GT:  WI(WI_F64X2_GT); break;
+        case ONYX_INTRINSIC_F64X2_LE:  WI(WI_F64X2_LE); break;
+        case ONYX_INTRINSIC_F64X2_GE:  WI(WI_F64X2_GE); break;
+
+        case ONYX_INTRINSIC_V128_NOT:       WI(WI_V128_NOT); break;
+        case ONYX_INTRINSIC_V128_AND:       WI(WI_V128_AND); break;
+        case ONYX_INTRINSIC_V128_ANDNOT:    WI(WI_V128_ANDNOT); break;
+        case ONYX_INTRINSIC_V128_OR:        WI(WI_V128_OR); break;
+        case ONYX_INTRINSIC_V128_XOR:       WI(WI_V128_XOR); break;
+        case ONYX_INTRINSIC_V128_BITSELECT: WI(WI_V128_BITSELECT); break;
+
+        case ONYX_INTRINSIC_I8X16_ABS:            WI(WI_I8X16_ABS); break;
+        case ONYX_INTRINSIC_I8X16_NEG:            WI(WI_I8X16_NEG); break;
+        case ONYX_INTRINSIC_I8X16_ANY_TRUE:       WI(WI_I8X16_ANY_TRUE); break;
+        case ONYX_INTRINSIC_I8X16_ALL_TRUE:       WI(WI_I8X16_ALL_TRUE); break;
+        case ONYX_INTRINSIC_I8X16_BITMASK:        WI(WI_I8X16_BITMASK); break;
+        case ONYX_INTRINSIC_I8X16_NARROW_I16X8_S: WI(WI_I8X16_NARROW_I16X8_S); break;
+        case ONYX_INTRINSIC_I8X16_NARROW_I16X8_U: WI(WI_I8X16_NARROW_I16X8_U); break;
+        case ONYX_INTRINSIC_I8X16_SHL:            WI(WI_I8X16_SHL); break;
+        case ONYX_INTRINSIC_I8X16_SHR_S:          WI(WI_I8X16_SHR_S); break;
+        case ONYX_INTRINSIC_I8X16_SHR_U:          WI(WI_I8X16_SHR_U); break;
+        case ONYX_INTRINSIC_I8X16_ADD:            WI(WI_I8X16_ADD); break;
+        case ONYX_INTRINSIC_I8X16_ADD_SAT_S:      WI(WI_I8X16_ADD_SAT_S); break;
+        case ONYX_INTRINSIC_I8X16_ADD_SAT_U:      WI(WI_I8X16_ADD_SAT_U); break;
+        case ONYX_INTRINSIC_I8X16_SUB:            WI(WI_I8X16_SUB); break;
+        case ONYX_INTRINSIC_I8X16_SUB_SAT_S:      WI(WI_I8X16_SUB_SAT_S); break;
+        case ONYX_INTRINSIC_I8X16_SUB_SAT_U:      WI(WI_I8X16_SUB_SAT_U); break;
+        case ONYX_INTRINSIC_I8X16_MIN_S:          WI(WI_I8X16_MIN_S); break;
+        case ONYX_INTRINSIC_I8X16_MIN_U:          WI(WI_I8X16_MIN_U); break;
+        case ONYX_INTRINSIC_I8X16_MAX_S:          WI(WI_I8X16_MAX_S); break;
+        case ONYX_INTRINSIC_I8X16_MAX_U:          WI(WI_I8X16_MAX_U); break;
+        case ONYX_INTRINSIC_I8X16_AVGR_U:         WI(WI_I8X16_AVGR_U); break;
+
+        case ONYX_INTRINSIC_I16X8_ABS:                WI(WI_I16X8_ABS); break;
+        case ONYX_INTRINSIC_I16X8_NEG:                WI(WI_I16X8_NEG); break;
+        case ONYX_INTRINSIC_I16X8_ANY_TRUE:           WI(WI_I16X8_ANY_TRUE); break;
+        case ONYX_INTRINSIC_I16X8_ALL_TRUE:           WI(WI_I16X8_ALL_TRUE); break;
+        case ONYX_INTRINSIC_I16X8_BITMASK:            WI(WI_I16X8_BITMASK); break;
+        case ONYX_INTRINSIC_I16X8_NARROW_I32X4_S:     WI(WI_I16X8_NARROW_I32X4_S); break;
+        case ONYX_INTRINSIC_I16X8_NARROW_I32X4_U:     WI(WI_I16X8_NARROW_I32X4_U); break;
+        case ONYX_INTRINSIC_I16X8_WIDEN_LOW_I8X16_S:  WI(WI_I16X8_WIDEN_LOW_I8X16_S); break;
+        case ONYX_INTRINSIC_I16X8_WIDEN_HIGH_I8X16_S: WI(WI_I16X8_WIDEN_HIGH_I8X16_S); break;
+        case ONYX_INTRINSIC_I16X8_WIDEN_LOW_I8X16_U:  WI(WI_I16X8_WIDEN_LOW_I8X16_U); break;
+        case ONYX_INTRINSIC_I16X8_WIDEN_HIGH_I8X16_U: WI(WI_I16X8_WIDEN_HIGH_I8X16_U); break;
+        case ONYX_INTRINSIC_I16X8_SHL:                WI(WI_I16X8_SHL); break;
+        case ONYX_INTRINSIC_I16X8_SHR_S:              WI(WI_I16X8_SHR_S); break;
+        case ONYX_INTRINSIC_I16X8_SHR_U:              WI(WI_I16X8_SHR_U); break;
+        case ONYX_INTRINSIC_I16X8_ADD:                WI(WI_I16X8_ADD); break;
+        case ONYX_INTRINSIC_I16X8_ADD_SAT_S:          WI(WI_I16X8_ADD_SAT_S); break;
+        case ONYX_INTRINSIC_I16X8_ADD_SAT_U:          WI(WI_I16X8_ADD_SAT_U); break;
+        case ONYX_INTRINSIC_I16X8_SUB:                WI(WI_I16X8_SUB); break;
+        case ONYX_INTRINSIC_I16X8_SUB_SAT_S:          WI(WI_I16X8_SUB_SAT_S); break;
+        case ONYX_INTRINSIC_I16X8_SUB_SAT_U:          WI(WI_I16X8_SUB_SAT_U); break;
+        case ONYX_INTRINSIC_I16X8_MUL:                WI(WI_I16X8_MUL); break;
+        case ONYX_INTRINSIC_I16X8_MIN_S:              WI(WI_I16X8_MIN_S); break;
+        case ONYX_INTRINSIC_I16X8_MIN_U:              WI(WI_I16X8_MIN_U); break;
+        case ONYX_INTRINSIC_I16X8_MAX_S:              WI(WI_I16X8_MAX_S); break;
+        case ONYX_INTRINSIC_I16X8_MAX_U:              WI(WI_I16X8_MAX_U); break;
+        case ONYX_INTRINSIC_I16X8_AVGR_U:             WI(WI_I16X8_AVGR_U); break;
+
+        case ONYX_INTRINSIC_I32X4_ABS:                WI(WI_I32X4_ABS); break;
+        case ONYX_INTRINSIC_I32X4_NEG:                WI(WI_I32X4_NEG); break;
+        case ONYX_INTRINSIC_I32X4_ANY_TRUE:           WI(WI_I32X4_ANY_TRUE); break;
+        case ONYX_INTRINSIC_I32X4_ALL_TRUE:           WI(WI_I32X4_ALL_TRUE); break;
+        case ONYX_INTRINSIC_I32X4_BITMASK:            WI(WI_I32X4_BITMASK); break;
+        case ONYX_INTRINSIC_I32X4_WIDEN_LOW_I16X8_S:  WI(WI_I32X4_WIDEN_LOW_I16X8_S); break;
+        case ONYX_INTRINSIC_I32X4_WIDEN_HIGH_I16X8_S: WI(WI_I32X4_WIDEN_HIGH_I16X8_S); break;
+        case ONYX_INTRINSIC_I32X4_WIDEN_LOW_I16X8_U:  WI(WI_I32X4_WIDEN_LOW_I16X8_U); break;
+        case ONYX_INTRINSIC_I32X4_WIDEN_HIGH_I16X8_U: WI(WI_I32X4_WIDEN_HIGH_I16X8_U); break;
+        case ONYX_INTRINSIC_I32X4_SHL:                WI(WI_I32X4_SHL); break;
+        case ONYX_INTRINSIC_I32X4_SHR_S:              WI(WI_I32X4_SHR_S); break;
+        case ONYX_INTRINSIC_I32X4_SHR_U:              WI(WI_I32X4_SHR_U); break;
+        case ONYX_INTRINSIC_I32X4_ADD:                WI(WI_I32X4_ADD); break;
+        case ONYX_INTRINSIC_I32X4_SUB:                WI(WI_I32X4_SUB); break;
+        case ONYX_INTRINSIC_I32X4_MUL:                WI(WI_I32X4_MUL); break;
+        case ONYX_INTRINSIC_I32X4_MIN_S:              WI(WI_I32X4_MIN_S); break;
+        case ONYX_INTRINSIC_I32X4_MIN_U:              WI(WI_I32X4_MIN_U); break;
+        case ONYX_INTRINSIC_I32X4_MAX_S:              WI(WI_I32X4_MAX_S); break;
+        case ONYX_INTRINSIC_I32X4_MAX_U:              WI(WI_I32X4_MAX_U); break;
+
+        case ONYX_INTRINSIC_I64X2_NEG:   WI(WI_I64X2_NEG); break;
+        case ONYX_INTRINSIC_I64X2_SHL:   WI(WI_I64X2_SHL); break;
+        case ONYX_INTRINSIC_I64X2_SHR_S: WI(WI_I64X2_SHR_S); break;
+        case ONYX_INTRINSIC_I64X2_SHR_U: WI(WI_I64X2_SHR_U); break;
+        case ONYX_INTRINSIC_I64X2_ADD:   WI(WI_I64X2_ADD); break;
+        case ONYX_INTRINSIC_I64X2_SUB:   WI(WI_I64X2_SUB); break;
+        case ONYX_INTRINSIC_I64X2_MUL:   WI(WI_I64X2_MUL); break;
+
+        case ONYX_INTRINSIC_F32X4_ABS:  WI(WI_F32X4_ABS); break;
+        case ONYX_INTRINSIC_F32X4_NEG:  WI(WI_F32X4_NEG); break;
+        case ONYX_INTRINSIC_F32X4_SQRT: WI(WI_F32X4_SQRT); break;
+        case ONYX_INTRINSIC_F32X4_ADD:  WI(WI_F32X4_ADD); break;
+        case ONYX_INTRINSIC_F32X4_SUB:  WI(WI_F32X4_SUB); break;
+        case ONYX_INTRINSIC_F32X4_MUL:  WI(WI_F32X4_MUL); break;
+        case ONYX_INTRINSIC_F32X4_DIV:  WI(WI_F32X4_DIV); break;
+        case ONYX_INTRINSIC_F32X4_MIN:  WI(WI_F32X4_MIN); break;
+        case ONYX_INTRINSIC_F32X4_MAX:  WI(WI_F32X4_MAX); break;
+
+        case ONYX_INTRINSIC_F64X2_ABS:  WI(WI_F64X2_ABS); break;
+        case ONYX_INTRINSIC_F64X2_NEG:  WI(WI_F64X2_NEG); break;
+        case ONYX_INTRINSIC_F64X2_SQRT: WI(WI_F64X2_SQRT); break;
+        case ONYX_INTRINSIC_F64X2_ADD:  WI(WI_F64X2_ADD); break;
+        case ONYX_INTRINSIC_F64X2_SUB:  WI(WI_F64X2_SUB); break;
+        case ONYX_INTRINSIC_F64X2_MUL:  WI(WI_F64X2_MUL); break;
+        case ONYX_INTRINSIC_F64X2_DIV:  WI(WI_F64X2_DIV); break;
+        case ONYX_INTRINSIC_F64X2_MIN:  WI(WI_F64X2_MIN); break;
+        case ONYX_INTRINSIC_F64X2_MAX:  WI(WI_F64X2_MAX); break;
+
+        case ONYX_INTRINSIC_I32X4_TRUNC_SAT_F32X4_S: WI(WI_I32X4_TRUNC_SAT_F32X4_S); break;
+        case ONYX_INTRINSIC_I32X4_TRUNC_SAT_F32X4_U: WI(WI_I32X4_TRUNC_SAT_F32X4_U); break;
+        case ONYX_INTRINSIC_F32X4_CONVERT_I32X4_S:   WI(WI_F32X4_CONVERT_I32X4_S); break;
+        case ONYX_INTRINSIC_F32X4_CONVERT_I32X4_U:   WI(WI_F32X4_CONVERT_I32X4_U); 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(WI_PTR_CONST, sub->elem_size);
+        WI(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(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;
+    WID(WI_PTR_CONST, memres->addr);
+    *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(WI_LOCAL_GET, local_offset);
+
+    } else {
+        WIL(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(WI_LOCAL_TEE, tmp_idx);
+
+        fori (i, 0, mem_count) {
+            type_linear_member_lookup(type, i, &two);
+            if (i != 0) WIL(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(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(WI_LOCAL_SET, temp_locals[i]);
+    }
+
+    if (!location_first) WIL(WI_LOCAL_SET, loc_idx);
+
+    fori (i, 0, elem_count) {
+        type_linear_member_lookup(type, i, &two);
+
+        u64 tmp_idx = temp_locals[i]; 
+        WIL(WI_LOCAL_GET, loc_idx);
+        WIL(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(WI_LOCAL_SET, rptr_local);
+    WIL(WI_LOCAL_SET, lptr_local);
+
+    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(WI_LOCAL_GET, lptr_local);
+
+            WIL(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(WI_LOCAL_GET, lptr_local);
+        WIL(WI_PTR_CONST, offset);
+        WI(WI_PTR_ADD);
+
+        WIL(WI_LOCAL_GET, rptr_local);
+        WIL(WI_I32_CONST, elem_count * elem_size);
+        WI(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(WI_PTR_CONST, 0);
+        WIL(WI_LOCAL_SET, offset_local);
+
+        WID(WI_BLOCK_START, 0x40);
+        WID(WI_LOOP_START, 0x40);
+            WIL(WI_LOCAL_GET, offset_local);
+            WIL(WI_LOCAL_GET, lptr_local);
+            WI(WI_PTR_ADD);
+
+            WIL(WI_LOCAL_GET, offset_local);
+            WIL(WI_LOCAL_GET, rptr_local);
+            WI(WI_PTR_ADD);
+
+            emit_load_instruction(mod, &code, elem_type, 0);
+            emit_store_instruction(mod, &code, elem_type, offset);
+
+            WIL(WI_LOCAL_GET, offset_local);
+            WIL(WI_PTR_CONST, elem_size);
+            WI(WI_PTR_ADD);
+            WIL(WI_LOCAL_TEE, offset_local);
+
+            WIL(WI_PTR_CONST, elem_count * elem_size);
+            WI(WI_PTR_GE);
+            WID(WI_COND_JUMP, 0x01);
+
+            WID(WI_JUMP, 0x00);
+
+        WI(WI_LOOP_END);
+        WI(WI_BLOCK_END);
+
+        local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+    }
+
+    local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+    local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+
+    *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(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(WI_LOCAL_GET, mod->stack_base_idx);
+    WIL(WI_PTR_CONST, local_offset);
+    WI(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 (!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(WI_LOCAL_SET, result_local);
+
+    offset = 0;
+    WI(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(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(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(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;
+
+    switch (expr->kind) {
+        case Ast_Kind_Param:
+        case Ast_Kind_Local: {
+            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;
+            WID(WI_PTR_CONST, memres->addr);
+            *offset_return = 0;
+            break;
+        }
+
+        default: {
+            onyx_report_error(expr->token->pos, "Unable to generate locate 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(WI_PTR_CONST, offset);
+        WI(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->type_id != 0) {
+            WID(WI_I32_CONST, ((AstType *) expr)->type_id);
+        } else {
+            Type* t = type_build_from_ast(context.ast_alloc, type);
+            WID(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(WI_LOCAL_GET, localidx + idx);
+
+                    } else {
+                        WIL(WI_LOCAL_GET, localidx);
+                    }
+                    break;
+                }
+
+                case Param_Pass_By_Implicit_Pointer: {
+                    WIL(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(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(WI_PTR_CONST, offset);
+                    WI(WI_PTR_ADD);
+                }
+            }
+
+            break;
+        }
+
+        case Ast_Kind_Global: {
+            i32 globalidx = (i32) bh_imap_get(&mod->index_map, (u64) expr);
+
+            WID(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;
+            }
+
+            bh_arr_push(code, instr);
+            break;
+        }
+
+        case Ast_Kind_StrLit: {
+            WID(WI_PTR_CONST, ((AstStrLit *) expr)->addr);
+            WID(WI_I32_CONST, ((AstStrLit *) expr)->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(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;
+            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;
+                    WIL(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(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(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(WI_LOCAL_SET, temporaries[i]);
+                    }
+
+                    fori (i, 0, idx) WI(WI_DROP);
+
+                    fori (i, 0, field_linear_members) {
+                        type_linear_member_lookup(field->type, i, &two);
+
+                        WIL(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(WI_DROP);
+            WIL(WI_LOCAL_SET, hi_local);
+            WIL(WI_LOCAL_TEE, lo_local);
+            if (sl->elem_size != 1) {
+                WID(WI_I32_CONST, sl->elem_size);
+                WI(WI_I32_MUL);
+            }
+            emit_expression(mod, &code, sl->addr);
+            WI(WI_I32_ADD);
+            WIL(WI_LOCAL_GET, hi_local);
+            WIL(WI_LOCAL_GET, lo_local);
+            WI(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(WI_I32_CONST, so->size);
+            break;
+        }
+
+        case Ast_Kind_Align_Of: {
+            AstAlignOf* ao = (AstAlignOf *) expr;
+            WID(WI_I32_CONST, ao->alignment);
+            break;
+        }
+
+        case Ast_Kind_Enum_Value: {
+            AstEnumValue* ev = (AstEnumValue *) expr;
+            WasmType backing_type = onyx_type_to_wasm_type(ev->type);
+            if      (backing_type == WASM_TYPE_INT32) WID(WI_I32_CONST, ev->value->value.i);
+            else if (backing_type == WASM_TYPE_INT64) WID(WI_I64_CONST, ev->value->value.l);
+            else onyx_report_error(ev->token->pos, "Invalid backing type for enum.");
+            break;
+        }
+
+        case Ast_Kind_Memres: {
+            AstMemRes* memres = (AstMemRes *) expr;
+            WID(WI_I32_CONST, memres->addr);
+            emit_load_instruction(mod, &code, memres->type, 0);
+            break;
+        }
+
+        case Ast_Kind_File_Contents: {
+            AstFileContents* fc = (AstFileContents *) expr;
+
+            assert(fc->addr > 0);
+            assert(fc->size > 0);
+
+            WID(WI_PTR_CONST, fc->addr);
+            WID(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;
+        }
+
+        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(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_NOP,         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_NOP,         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(WI_DROP);
+        *pcode = code;
+        return;
+    }
+
+    if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_Array) {
+        WID(WI_I32_CONST, from->Array.count);
+        *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;
+    }
+
+    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(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(WI_LOCAL_SET, dest_loc);
+
+        } else if (mod->curr_cc == CC_Return_Stack) {
+            WIL(WI_LOCAL_GET, mod->stack_base_idx);
+            WID(WI_I32_CONST, type_size_of(ret->expr->type));
+            WI(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);
+
+    // 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--;
+        }
+    }
+
+    i64 jump_label = get_structured_jump_label(mod, Jump_Type_Return, 1);
+    if (jump_label >= 0) {
+        WIL(WI_JUMP, jump_label);
+
+    } else {
+        // Make a patch for the two instructions needed to restore the stack pointer
+        SUBMIT_PATCH(mod->stack_leave_patches, 0);
+        WI(WI_NOP);
+        WI(WI_NOP);
+
+        WI(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(WI_I32_CONST, 0); break;
+        case WASM_TYPE_INT64:   WIL(WI_I64_CONST, 0); break;
+        case WASM_TYPE_FLOAT32: WIL(WI_F32_CONST, 0); break;
+        case WASM_TYPE_FLOAT64: WIL(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(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(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, "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);
+    *(t++) = (char) return_type;
+    *t = '\0';
+
+    i32 type_idx = 0;
+    if (bh_table_has(i32, mod->type_map, type_repr_buf)) {
+        type_idx = bh_table_get(i32, mod->type_map, type_repr_buf);
+    } 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;
+
+        // HACK ish thing
+        memcpy(type->param_types, type_repr_buf, type->param_count);
+
+        bh_arr_push(mod->types, type);
+
+        bh_table_put(i32, 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 inline b32 should_emit_function(AstFunction* fd) {
+    // NOTE: Don't output intrinsic functions
+    if (fd->flags & Ast_Flag_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->flags & Ast_Flag_Exported) {
+            return 1;
+        } else {
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+
+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 = {
+        .type_idx = type_idx,
+        .locals = {
+            .param_count = 0,
+
+            .allocated = { 0 },
+            .freed     = { 0 },
+
+            .max_stack = 0,
+            .curr_stack = 0,
+        },
+        .code = NULL,
+    };
+
+    bh_arr_new(mod->allocator, wasm_func.code, 4);
+
+    i32 func_idx = (i32) bh_imap_get(&mod->index_map, (u64) fd);
+
+    // If there is no body then don't process the code
+    if (fd->body != NULL) {
+        // 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)) {
+                        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: {
+                    bh_imap_put(&mod->local_map, (u64) param->local, localidx++ | LOCAL_IS_WASM);
+                    break;
+                }
+
+                default: assert(0);
+            }
+        }
+
+        mod->local_alloc = &wasm_func.locals;
+        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);
+
+        bh_arr_insert_end(wasm_func.code, 5);
+        fori (i, 0, 5) wasm_func.code[i] = (WasmInstruction) { WI_NOP, 0 };
+
+        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.
+            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);
+
+    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);
+
+    // NOTE: Clear the local map on exit of generating this function
+    bh_imap_clear(&mod->local_map);
+}
+
+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  = fd->foreign_module,
+        .name = fd->foreign_name,
+    };
+
+    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;
+    }
+
+    bh_table_put(WasmExport, 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 - module->foreign_global_count, glob);
+
+    if (global->flags & Ast_Flag_Global_Stack_Top)
+        module->stack_top_ptr = &module->globals[global_idx - module->foreign_global_count].initial_value[0].data.i1;
+}
+
+static void emit_foreign_global(OnyxWasmModule* module, AstGlobal* global) {
+    WasmType global_type = onyx_type_to_wasm_type(global->type);
+
+    if (global->flags & Ast_Flag_Foreign) {
+        WasmImport import = {
+            .kind = WASM_FOREIGN_GLOBAL,
+            .idx  = global_type,
+            .mod  = global->foreign_module,
+            .name = global->foreign_name,
+        };
+
+        bh_arr_push(module->imports, import);
+    }
+}
+
+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);
+
+    // Warning for having '%' in a string literal (because that probably is being used for a old print format)
+    /*
+    if (charset_contains((const char *) strdata, '%')) {
+        onyx_report_warning(strlit->token->pos, "Found string literal with '%%'");
+    }
+    */
+
+    if (bh_table_has(StrLitInfo, mod->string_literals, (char *) strdata)) {
+        StrLitInfo sti = bh_table_get(StrLitInfo, mod->string_literals, (char *) strdata);
+        strlit->addr   = sti.addr;
+        strlit->length = sti.len;
+
+        bh_free(global_heap_allocator, strdata);
+        return;
+    }
+
+    WasmDatum datum = {
+        .offset = mod->next_datum_offset,
+        .length = length,
+        .data = strdata,
+    };
+
+    strlit->addr = (u32) mod->next_datum_offset,
+    strlit->length = length;
+    mod->next_datum_offset += length;
+
+    bh_table_put(StrLitInfo, mod->string_literals, (char *) strdata, ((StrLitInfo) { strlit->addr, strlit->length }));
+
+    bh_arr_push(mod->data, datum);
+}
+
+static void emit_raw_data(OnyxWasmModule* mod, ptr data, AstTyped* node) {
+    if (!emit_raw_data_(mod, data, node)) {
+        onyx_report_error(node->token->pos,
+            "Cannot generate constant data for '%s'.",
+            onyx_ast_node_kind_string(node->kind));
+    }
+}
+
+static b32 emit_raw_data_(OnyxWasmModule* mod, ptr data, AstTyped* node) {
+    b32 retval = 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_raw_data_(mod, bh_pointer_add(data, i * elem_size), *expr);
+            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_raw_data_(mod, bh_pointer_add(data, smem.offset), sl->args.values[i]);
+        }
+
+        break;
+    }
+
+    case Ast_Kind_StrLit: {
+        AstStrLit* sl = (AstStrLit *) node;
+
+        // NOTE: This assumes the address and the length fields have been filled out
+        // by emit_string_literal.
+        u32* sdata = (u32 *) data;
+        sdata[0] = sl->addr;
+        sdata[1] = 0x00;
+        sdata[2] = sl->length;
+        break;
+    }
+
+    case Ast_Kind_Enum_Value: {
+        AstEnumValue* ev = (AstEnumValue *) node;
+        retval &= emit_raw_data_(mod, data, (AstTyped *) ev->value);
+        break;
+    }
+
+    case Ast_Kind_Function: {
+        AstFunction* func = (AstFunction *) node;
+        *((u32 *) data) = get_element_idx(mod, func);
+        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:
+            *((i8 *) data) = (i8) ((AstNumLit *) node)->value.i;
+            return retval;
+
+        case Basic_Kind_I16:
+        case Basic_Kind_U16:
+            *((i16 *) data) = (i16) ((AstNumLit *) node)->value.i;
+            return retval;
+
+        case Basic_Kind_I32:
+        case Basic_Kind_U32:
+            *((i32 *) data) = ((AstNumLit *) node)->value.i;
+            return retval;
+
+        case Basic_Kind_I64:
+        case Basic_Kind_U64:
+        case Basic_Kind_Rawptr:
+            *((i64 *) data) = ((AstNumLit *) node)->value.l;
+            return retval;
+
+        case Basic_Kind_F32:
+            *((f32 *) data) = ((AstNumLit *) node)->value.f;
+            return retval;
+
+        case Basic_Kind_F64:
+            *((f64 *) data) = ((AstNumLit *) node)->value.d;
+            return retval;
+
+        default: break;
+        }
+
+        //fallthrough
+    }
+
+    case Ast_Kind_Code_Block: break;
+
+    default: retval = 0;
+    }
+
+    return retval;
+}
+
+static void emit_memory_reservation(OnyxWasmModule* mod, AstMemRes* memres) {
+    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->addr = table_location;
+
+        return;
+    }
+
+    u32 offset = mod->next_datum_offset;
+    bh_align(offset, alignment);
+
+    if (memres->initial_value != NULL) {
+        u8* data = bh_alloc(global_heap_allocator, size);
+        emit_raw_data(mod, data, memres->initial_value);
+
+        WasmDatum datum = {
+            .offset = offset,
+            .length = size,
+            .data = data,
+        };
+
+        bh_arr_push(mod->data, datum);
+    }
+
+    memres->addr = offset;
+    mod->next_datum_offset = offset + size;
+}
+
+static void emit_file_contents(OnyxWasmModule* mod, AstFileContents* fc) {
+    token_toggle_end(fc->filename_token);
+
+    // 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);
+
+        char* temp_fn     = bh_alloc_array(global_scratch_allocator, char, fc->filename_token->length);
+        i32   temp_fn_len = string_process_escape_seqs(temp_fn, fc->filename_token->text, fc->filename_token->length);
+        char* filename    = lookup_included_file(temp_fn, parent_folder, 0, 0);
+        fc->filename      = bh_strdup(global_heap_allocator, filename);
+    }
+
+    token_toggle_end(fc->filename_token);
+
+    if (bh_table_has(StrLitInfo, mod->loaded_file_info, fc->filename)) {
+        StrLitInfo info = bh_table_get(StrLitInfo, mod->loaded_file_info, fc->filename);
+        fc->addr = info.addr;
+        fc->size = info.len;
+        return;
+    }
+
+    u32 offset = mod->next_datum_offset;
+    bh_align(offset, 16);
+
+    if (!bh_file_exists(fc->filename)) {
+        onyx_report_error(fc->filename_token->pos,
+                "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);
+
+    bh_table_put(StrLitInfo, mod->loaded_file_info, fc->filename, ((StrLitInfo) {
+        .addr = offset,
+        .len  = length - 1,
+    }));
+
+    fc->addr = offset;
+    fc->size = length - 1;
+
+    WasmDatum datum = {
+        .offset = offset,
+        .length = length,
+        .data = actual_data,
+    };
+
+    bh_arr_push(mod->data, datum);
+
+    mod->next_datum_offset = offset + length;
+}
+
+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,
+        .next_datum_offset = 32, // Starting offset so null pointers don't immediately
+                                 // break constant data.       - brendanfh 2020/12/16
+
+        .elems = NULL,
+        .next_elem_idx = 0,
+
+        .structured_jump_target = NULL,
+        .return_location_stack = NULL,
+        .local_allocations = NULL,
+        .stack_leave_patches = NULL,
+        .deferred_stmts = NULL,
+
+        .stack_top_ptr = NULL,
+        .stack_base_idx = 0,
+
+        .foreign_function_count = 0,
+        .foreign_global_count = 0,
+
+        .null_proc_func_idx = -1,
+    };
+
+    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(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);
+
+    bh_table_init(global_heap_allocator, module.type_map, 61);
+    bh_table_init(global_heap_allocator, module.exports, 61);
+    bh_table_init(global_heap_allocator, module.loaded_file_info, 7);
+    bh_table_init(global_heap_allocator, module.string_literals, 16);
+
+    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);
+
+    WasmExport mem_export = {
+        .kind = WASM_FOREIGN_MEMORY,
+        .idx = 0,
+    };
+    // :ArbitraryConstant
+    // :WasmMemory
+    bh_table_put(WasmExport, module.exports, "memory", mem_export);
+    module.export_count++;
+
+    return module;
+}
+
+void emit_entity(Entity* ent) {
+    OnyxWasmModule* module = context.wasm_module;
+
+    if (module->stack_top_ptr) {
+        *module->stack_top_ptr = module->next_datum_offset;
+
+        if (*module->stack_top_ptr % 16 != 0) {
+            *module->stack_top_ptr += 16 - (*module->stack_top_ptr % 16);
+        }
+
+        builtin_heap_start.value.i = *module->stack_top_ptr + (1 << 20);
+        if (builtin_heap_start.value.i % 16 != 0) {
+            builtin_heap_start.value.i += 16 - (builtin_heap_start.value.i % 16);
+        }
+    }
+
+    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);
+            }
+            break;
+
+        case Entity_Type_Foreign_Global_Header:
+            module->foreign_global_count++;
+            emit_foreign_global(module, ent->global);
+            // fallthrough
+
+        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);
+            }
+            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_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);
+    bh_table_free(module->type_map);
+    bh_table_free(module->exports);
+}
+
+
+#include "wasm_output.c"
diff --git a/src/wasm_intrinsics.c b/src/wasm_intrinsics.c
new file mode 100644 (file)
index 0000000..b39fad8
--- /dev/null
@@ -0,0 +1,153 @@
+// 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(WI_LOCAL_SET, count_local);
+    WIL(WI_LOCAL_SET, source_local);
+    WIL(WI_LOCAL_SET, dest_local);
+    
+    // count is greater than 0
+    WIL(WI_LOCAL_GET, count_local);
+    WID(WI_I32_CONST, 0);
+    WI(WI_I32_GT_S);
+    
+    WID(WI_IF_START, 0x40);
+    WID(WI_LOOP_START, 0x40);
+    
+    WIL(WI_LOCAL_GET, count_local);
+    WID(WI_I32_CONST, 1);
+    WI(WI_I32_SUB);
+    WIL(WI_LOCAL_SET, count_local);
+    
+    WIL(WI_LOCAL_GET, dest_local);
+    WIL(WI_LOCAL_GET, count_local);
+    WI(WI_PTR_ADD);
+    
+    WIL(WI_LOCAL_GET, source_local);
+    WIL(WI_LOCAL_GET, count_local);
+    WI(WI_PTR_ADD);
+    
+    WID(WI_I32_LOAD_8_U, ((WasmInstructionData) { 0, 0 }));
+    WID(WI_I32_STORE_8, ((WasmInstructionData) { 0, 0 }));
+    
+    WIL(WI_LOCAL_GET, count_local);
+    WID(WI_I32_CONST, 0);
+    WI(WI_I32_GT_S);
+    WID(WI_COND_JUMP, 0x00);
+    
+    WI(WI_LOOP_END);
+    WI(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(WI_LOCAL_SET, count_local);
+    WIL(WI_LOCAL_SET, byte_local);
+    WIL(WI_LOCAL_SET, dest_local);
+    
+    // count is greater than 0
+    WIL(WI_LOCAL_GET, count_local);
+    WID(WI_I32_CONST, 0);
+    WI(WI_I32_GT_S);
+    
+    WID(WI_IF_START, 0x40);
+    WID(WI_LOOP_START, 0x40);
+    
+    WIL(WI_LOCAL_GET, count_local);
+    WID(WI_I32_CONST, 1);
+    WI(WI_I32_SUB);
+    WIL(WI_LOCAL_SET, count_local);
+    
+    WIL(WI_LOCAL_GET, dest_local);
+    WIL(WI_LOCAL_GET, count_local);
+    WI(WI_PTR_ADD);
+    
+    WIL(WI_LOCAL_GET, byte_local);
+    WID(WI_I32_STORE_8, ((WasmInstructionData) { 0, 0 }));
+    
+    WIL(WI_LOCAL_GET, count_local);
+    WID(WI_I32_CONST, 0);
+    WI(WI_I32_GT_S);
+    WID(WI_COND_JUMP, 0x00);
+    
+    WI(WI_LOOP_END);
+    WI(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(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(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,
+                    "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;
+}
diff --git a/src/wasm_output.c b/src/wasm_output.c
new file mode 100644 (file)
index 0000000..e1c5764
--- /dev/null
@@ -0,0 +1,621 @@
+// This file is included in src/onyxwasm.c.
+// It is separated because of its fundamentally different goals.
+
+//-------------------------------------------------
+// BINARY OUPUT
+//-------------------------------------------------
+
+#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_CODE 10
+#define WASM_SECTION_ID_DATA 11
+
+typedef i32 vector_func(void*, bh_buffer*);
+
+static const u8 WASM_MAGIC_STRING[] = { 0x00, 0x61, 0x73, 0x6D };
+static const u8 WASM_VERSION[] = { 0x01, 0x00, 0x00, 0x00 };
+
+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, bh_buffer* buff) {
+    i32 leb_len, prev_len = buff->length;
+    u8* leb;
+    
+    bh_buffer_write_byte(buff, (max >= 0) ? 0x01 : 0x00);
+    
+    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, &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) {
+    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);
+    
+    // FIXME: This needs to be dynamically chosen depending on the size of
+    // the data section and stack size pre-requeseted.
+    // :WasmMemory
+    output_limits(1024, -1, &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->text, import->mod->length, &vec_buff);
+        output_name(import->name->text, import->name->length, &vec_buff);
+        bh_buffer_write_byte(&vec_buff, (u8) import->kind);
+        
+        leb = uint_to_uleb128((u64) import->idx, &leb_len);
+        bh_buffer_append(&vec_buff, leb, leb_len);
+        
+        if (import->kind == WASM_FOREIGN_GLOBAL) {
+            // NOTE: All foreign globals are mutable
+            bh_buffer_write_byte(&vec_buff, 0x01);
+        }
+    }
+    
+    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;
+    bh_table_each_start(WasmExport, module->exports);
+    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);
+    bh_table_each_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_startsection(OnyxWasmModule* module, bh_buffer* buff) {
+    i32 prev_len = buff->length;
+    
+    i32 start_idx = -1;
+    bh_table_each_start(WasmExport, module->exports) {
+        if (value.kind == WASM_FOREIGN_FUNCTION) {
+            if (strncmp("main", key, 5) == 0) {
+                start_idx = value.idx;
+                break;
+            }
+        }
+    } bh_table_each_end;
+    
+    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_NOP) 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 {
+        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_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);
+    
+    // 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);
+    
+    // DEBUG_HERE;
+    
+    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_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) {
+        if (datum->data == NULL) continue;
+        
+        // NOTE: 0x00 memory index
+        bh_buffer_write_byte(&vec_buff, 0x00);
+        
+        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);
+        
+        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]);
+    }
+    
+    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;
+}
+
+void onyx_wasm_module_write_to_file(OnyxWasmModule* module, bh_file file) {
+    bh_buffer master_buffer;
+    bh_buffer_init(&master_buffer, global_heap_allocator, 128);
+    bh_buffer_append(&master_buffer, WASM_MAGIC_STRING, 4);
+    bh_buffer_append(&master_buffer, WASM_VERSION, 4);
+    
+    output_typesection(module, &master_buffer);
+    output_importsection(module, &master_buffer);
+    output_funcsection(module, &master_buffer);
+    output_tablesection(module, &master_buffer);
+    output_memorysection(module, &master_buffer);
+    output_globalsection(module, &master_buffer);
+    output_exportsection(module, &master_buffer);
+    output_startsection(module, &master_buffer);
+    output_elemsection(module, &master_buffer);
+    output_codesection(module, &master_buffer);
+    output_datasection(module, &master_buffer);
+    
+    bh_file_write(&file, master_buffer.data, master_buffer.length);
+}
diff --git a/src/wasm_type_table.c b/src/wasm_type_table.c
new file mode 100644 (file)
index 0000000..4daf9d1
--- /dev/null
@@ -0,0 +1,374 @@
+// This file is directly included in src/onxywasm.c
+// It is here purely to decrease the amount of clutter in the main file.
+
+
+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))
+
+    // This is the data behind the "type_table" slice in type_info.onyx
+    u32 type_count = bh_arr_length(type_map.entries) + 1;
+    u64* table_info = bh_alloc_array(global_heap_allocator, u64, type_count); // HACK
+    memset(table_info, 0, type_count * sizeof(u64));
+
+    bh_buffer table_buffer;
+    bh_buffer_init(&table_buffer, global_heap_allocator, 4096);
+
+    // 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.ptr_to_data->Pointer.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.ptr_to_data->Pointer.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.ptr_to_data->Pointer.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));
+                bh_buffer_align(&table_buffer, 8);
+                PATCH;
+                bh_buffer_write_u64(&table_buffer, components_base);
+                bh_buffer_write_u64(&table_buffer, 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);
+
+                PATCH;
+                bh_buffer_write_u64(&table_buffer, parameters_base);
+                bh_buffer_write_u64(&table_buffer, 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);
+                    PATCH;
+                    bh_buffer_write_u64(&table_buffer, name_loc);
+                    bh_buffer_write_u64(&table_buffer, (*value)->token->length);
+                    bh_buffer_write_u64(&table_buffer, (*value)->value->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);
+                PATCH;
+                bh_buffer_write_u64(&table_buffer, name_base);
+                bh_buffer_write_u64(&table_buffer, name_length);
+                PATCH;
+                bh_buffer_write_u64(&table_buffer, member_base);
+                bh_buffer_write_u64(&table_buffer, 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);
+                memset(value_locations, 0, s->mem_count * sizeof(u32));
+
+                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);
+
+                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);
+                            u8* buffer = table_buffer.data + table_buffer.length;
+                            emit_raw_data(module, buffer, sln->value);
+                            table_buffer.length += size;
+                            break;
+                        }
+
+                        default: {
+                            // Set to null if this is not known how to encode
+                            param_locations[i-1] = 0;
+                            break;
+                        }
+                    }
+                }
+
+                bh_buffer_align(&table_buffer, 8);
+
+                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);
+                    u8* buffer = table_buffer.data + table_buffer.length;
+
+                    if (!emit_raw_data_(module, buffer, value)) {
+                        // 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;
+                    }
+                }
+
+                bh_buffer_align(&table_buffer, 8);
+                u32 members_base = table_buffer.length;
+
+                i = 0;
+                bh_arr_each(StructMember*, pmem, s->memarr) {
+                    StructMember* mem = *pmem;
+
+                    u32 name_loc = name_locations[i];
+                    u32 value_loc = value_locations[i++];
+
+                    bh_buffer_align(&table_buffer, 8);
+                    PATCH;
+                    bh_buffer_write_u64(&table_buffer, name_loc);
+                    bh_buffer_write_u64(&table_buffer, 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);
+                    
+                    bh_buffer_align(&table_buffer, 8);
+                    PATCH;
+                    bh_buffer_write_u64(&table_buffer, value_loc);
+                }
+
+                bh_buffer_align(&table_buffer, 8);
+                u32 params_base = table_buffer.length;
+
+                i = 0;
+                bh_arr_each(AstPolySolution, sln, s->poly_sln) {
+                    bh_buffer_align(&table_buffer, 8);
+                    PATCH;
+                    bh_buffer_write_u64(&table_buffer, 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);
+                }
+
+                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));
+                bh_buffer_write_u32(&table_buffer, 0);
+                PATCH;
+                bh_buffer_write_u64(&table_buffer, name_base);
+                bh_buffer_write_u64(&table_buffer, name_length);
+                PATCH;
+                bh_buffer_write_u64(&table_buffer, members_base);
+                bh_buffer_write_u64(&table_buffer, s->mem_count);
+                PATCH;
+                bh_buffer_write_u64(&table_buffer, params_base);
+                bh_buffer_write_u64(&table_buffer, bh_arr_length(s->poly_sln));
+
+                break;
+            }
+        }
+    }
+
+    u32 offset = module->next_datum_offset;
+    bh_align(offset, 8);
+
+    u64 type_table_location = offset;
+
+    WasmDatum type_table_data = {
+        .offset = offset,
+        .length = type_count * 8,
+        .data = table_info,
+    };
+    bh_arr_push(module->data, type_table_data);
+
+    offset += type_table_data.length;
+
+    fori (i, 0, type_count) {
+        table_info[i] += offset;
+    }
+
+    bh_arr_each(u32, patch_loc, base_patch_locations) {
+        u64* loc = bh_pointer_add(table_buffer.data, *patch_loc);
+        if (*loc == 0) continue;
+        
+        *loc += offset;
+    }
+
+    WasmDatum type_info_data = {
+        .offset = offset,
+        .length = table_buffer.length,
+        .data = table_buffer.data,
+    };
+    bh_arr_push(module->data, type_info_data);
+    offset += type_info_data.length;
+
+    u64 global_data_ptr = offset;
+
+    u64* tmp_data = bh_alloc(global_heap_allocator, 16);
+    tmp_data[0] = type_table_location;
+    tmp_data[1] = type_count;
+    WasmDatum type_table_global_data = {
+        .offset = offset,
+        .length = 16,
+        .data = tmp_data,
+    };
+    bh_arr_push(module->data, type_table_global_data);
+    offset += type_table_global_data.length;
+
+    module->next_datum_offset = offset;
+
+    return global_data_ptr;
+
+#undef PATCH
+}