Commit 3ef2f7058b

Andrew Kelley <superjoe30@gmail.com>
2016-01-10 23:13:39
refactor out the horrible beast that was codegen_node
1 parent d4b8852
src/all_types.hpp
@@ -0,0 +1,928 @@
+/*
+ * Copyright (c) 2015 Andrew Kelley
+ *
+ * This file is part of zig, which is MIT licensed.
+ * See http://opensource.org/licenses/MIT
+ */
+
+#ifndef ZIG_ALL_TYPES_HPP
+#define ZIG_ALL_TYPES_HPP
+
+#include "list.hpp"
+#include "buffer.hpp"
+#include "zig_llvm.hpp"
+#include "hash_map.hpp"
+#include "errmsg.hpp"
+
+struct AstNode;
+struct ImportTableEntry;
+struct AsmToken;
+struct FnTableEntry;
+struct BlockContext;
+struct TypeTableEntry;
+struct VariableTableEntry;
+struct Cast;
+struct BuiltinFnEntry;
+struct LabelTableEntry;
+struct TypeStructField;
+struct CodeGen;
+
+enum OutType {
+    OutTypeUnknown,
+    OutTypeExe,
+    OutTypeLib,
+    OutTypeObj,
+};
+
+enum CodeGenBuildType {
+    CodeGenBuildTypeDebug,
+    CodeGenBuildTypeRelease,
+};
+
+enum CastOp {
+    CastOpNothing,
+    CastOpPtrToInt,
+    CastOpIntWidenOrShorten,
+    CastOpToUnknownSizeArray,
+    CastOpMaybeWrap,
+    CastOpPointerReinterpret,
+};
+
+struct Cast {
+    CastOp op;
+    // if op is CastOpArrayToString, this will be a pointer to
+    // the string struct on the stack
+    LLVMValueRef ptr;
+    TypeTableEntry *after_type;
+    AstNode *source_node;
+};
+
+struct Expr {
+    TypeTableEntry *type_entry;
+    // the context in which this expression is evaluated.
+    // for blocks, this points to the containing scope, not the block's own scope for its children.
+    BlockContext *block_context;
+
+    // may be null for no cast
+    Cast implicit_cast; // happens first
+    Cast implicit_maybe_cast; // happens second
+};
+
+struct NumLitCodeGen {
+    TypeTableEntry *resolved_type;
+};
+
+struct StructValExprCodeGen {
+    TypeTableEntry *type_entry;
+    LLVMValueRef ptr;
+    AstNode *source_node;
+};
+
+struct TopLevelDecl {
+    // reminder: hash tables must be initialized before use
+    HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> deps;
+    Buf *name;
+    ImportTableEntry *import;
+    // set this flag temporarily to detect infinite loops
+    bool in_current_deps;
+};
+
+enum NodeType {
+    NodeTypeRoot,
+    NodeTypeRootExportDecl,
+    NodeTypeFnProto,
+    NodeTypeFnDef,
+    NodeTypeFnDecl,
+    NodeTypeParamDecl,
+    NodeTypeType,
+    NodeTypeBlock,
+    NodeTypeExternBlock,
+    NodeTypeDirective,
+    NodeTypeReturnExpr,
+    NodeTypeVariableDeclaration,
+    NodeTypeBinOpExpr,
+    NodeTypeCastExpr,
+    NodeTypeNumberLiteral,
+    NodeTypeStringLiteral,
+    NodeTypeCharLiteral,
+    NodeTypeUnreachable,
+    NodeTypeSymbol,
+    NodeTypePrefixOpExpr,
+    NodeTypeFnCallExpr,
+    NodeTypeArrayAccessExpr,
+    NodeTypeSliceExpr,
+    NodeTypeFieldAccessExpr,
+    NodeTypeUse,
+    NodeTypeVoid,
+    NodeTypeBoolLiteral,
+    NodeTypeNullLiteral,
+    NodeTypeIfBoolExpr,
+    NodeTypeIfVarExpr,
+    NodeTypeWhileExpr,
+    NodeTypeLabel,
+    NodeTypeGoto,
+    NodeTypeBreak,
+    NodeTypeContinue,
+    NodeTypeAsmExpr,
+    NodeTypeStructDecl,
+    NodeTypeStructField,
+    NodeTypeStructValueExpr,
+    NodeTypeStructValueField,
+    NodeTypeEnumDecl,
+    NodeTypeEnumField,
+    NodeTypeCompilerFnExpr,
+    NodeTypeCompilerFnType,
+};
+
+struct AstNodeRoot {
+    ZigList<AstNode *> top_level_decls;
+};
+
+enum VisibMod {
+    VisibModPrivate,
+    VisibModPub,
+    VisibModExport,
+};
+
+struct AstNodeFnProto {
+    ZigList<AstNode *> *directives;
+    VisibMod visib_mod;
+    Buf name;
+    ZigList<AstNode *> params;
+    AstNode *return_type;
+    bool is_var_args;
+
+    // populated by semantic analyzer:
+
+    // the extern block this fn proto is inside. can be null.
+    AstNode *extern_node;
+    // the struct decl node this fn proto is inside. can be null.
+    AstNode *struct_node;
+    // the function definition this fn proto is inside. can be null.
+    AstNode *fn_def_node;
+    FnTableEntry *fn_table_entry;
+    bool skip;
+    TopLevelDecl top_level_decl;
+};
+
+struct AstNodeFnDef {
+    AstNode *fn_proto;
+    AstNode *body;
+
+    // populated by semantic analyzer
+    TypeTableEntry *implicit_return_type;
+    BlockContext *block_context;
+};
+
+struct AstNodeFnDecl {
+    AstNode *fn_proto;
+};
+
+struct AstNodeParamDecl {
+    Buf name;
+    AstNode *type;
+
+    // populated by semantic analyzer
+    VariableTableEntry *variable;
+};
+
+enum AstNodeTypeType {
+    AstNodeTypeTypePrimitive,
+    AstNodeTypeTypePointer,
+    AstNodeTypeTypeArray,
+    AstNodeTypeTypeMaybe,
+    AstNodeTypeTypeCompilerExpr,
+};
+
+struct AstNodeType {
+    AstNodeTypeType type;
+    Buf primitive_name;
+    AstNode *child_type;
+    AstNode *array_size; // can be null
+    bool is_const;
+    bool is_noalias;
+    AstNode *compiler_expr;
+
+    // populated by semantic analyzer
+    TypeTableEntry *entry;
+};
+
+struct AstNodeBlock {
+    ZigList<AstNode *> statements;
+
+    // populated by semantic analyzer
+    BlockContext *block_context;
+    Expr resolved_expr;
+};
+
+struct AstNodeReturnExpr {
+    // might be null in case of return void;
+    AstNode *expr;
+
+    // populated by semantic analyzer:
+    Expr resolved_expr;
+};
+
+struct AstNodeVariableDeclaration {
+    Buf symbol;
+    bool is_const;
+    VisibMod visib_mod;
+    // one or both of type and expr will be non null
+    AstNode *type;
+    AstNode *expr;
+
+    // populated by semantic analyzer
+    TopLevelDecl top_level_decl;
+    Expr resolved_expr;
+};
+
+enum BinOpType {
+    BinOpTypeInvalid,
+    BinOpTypeAssign,
+    BinOpTypeAssignTimes,
+    BinOpTypeAssignDiv,
+    BinOpTypeAssignMod,
+    BinOpTypeAssignPlus,
+    BinOpTypeAssignMinus,
+    BinOpTypeAssignBitShiftLeft,
+    BinOpTypeAssignBitShiftRight,
+    BinOpTypeAssignBitAnd,
+    BinOpTypeAssignBitXor,
+    BinOpTypeAssignBitOr,
+    BinOpTypeAssignBoolAnd,
+    BinOpTypeAssignBoolOr,
+    BinOpTypeBoolOr,
+    BinOpTypeBoolAnd,
+    BinOpTypeCmpEq,
+    BinOpTypeCmpNotEq,
+    BinOpTypeCmpLessThan,
+    BinOpTypeCmpGreaterThan,
+    BinOpTypeCmpLessOrEq,
+    BinOpTypeCmpGreaterOrEq,
+    BinOpTypeBinOr,
+    BinOpTypeBinXor,
+    BinOpTypeBinAnd,
+    BinOpTypeBitShiftLeft,
+    BinOpTypeBitShiftRight,
+    BinOpTypeAdd,
+    BinOpTypeSub,
+    BinOpTypeMult,
+    BinOpTypeDiv,
+    BinOpTypeMod,
+    BinOpTypeUnwrapMaybe,
+};
+
+struct AstNodeBinOpExpr {
+    AstNode *op1;
+    BinOpType bin_op;
+    AstNode *op2;
+
+    // populated by semantic analyzer:
+    // for when op is BinOpTypeAssign
+    VariableTableEntry *var_entry;
+    Expr resolved_expr;
+};
+
+struct AstNodeFnCallExpr {
+    AstNode *fn_ref_expr;
+    ZigList<AstNode *> params;
+    bool is_builtin;
+
+    // populated by semantic analyzer:
+    BuiltinFnEntry *builtin_fn;
+    Expr resolved_expr;
+};
+
+struct AstNodeArrayAccessExpr {
+    AstNode *array_ref_expr;
+    AstNode *subscript;
+
+    // populated by semantic analyzer:
+    Expr resolved_expr;
+};
+
+struct AstNodeSliceExpr {
+    AstNode *array_ref_expr;
+    AstNode *start;
+    AstNode *end;
+    bool is_const;
+
+    // populated by semantic analyzer:
+    Expr resolved_expr;
+    StructValExprCodeGen resolved_struct_val_expr;
+};
+
+struct AstNodeFieldAccessExpr {
+    AstNode *struct_expr;
+    Buf field_name;
+
+    // populated by semantic analyzer
+    int field_index;
+    TypeStructField *type_struct_field;
+    Expr resolved_expr;
+};
+
+struct AstNodeExternBlock {
+    ZigList<AstNode *> *directives;
+    ZigList<AstNode *> fn_decls;
+};
+
+struct AstNodeDirective {
+    Buf name;
+    Buf param;
+};
+
+struct AstNodeRootExportDecl {
+    Buf type;
+    Buf name;
+    ZigList<AstNode *> *directives;
+};
+
+struct AstNodeCastExpr {
+    AstNode *expr;
+    AstNode *type;
+
+    // populated by semantic analyzer
+    Cast cast;
+    Expr resolved_expr;
+};
+
+enum PrefixOp {
+    PrefixOpInvalid,
+    PrefixOpBoolNot,
+    PrefixOpBinNot,
+    PrefixOpNegation,
+    PrefixOpAddressOf,
+    PrefixOpConstAddressOf,
+    PrefixOpDereference,
+};
+
+struct AstNodePrefixOpExpr {
+    PrefixOp prefix_op;
+    AstNode *primary_expr;
+
+    // populated by semantic analyzer
+    Expr resolved_expr;
+};
+
+struct AstNodeUse {
+    Buf path;
+    ZigList<AstNode *> *directives;
+
+    // populated by semantic analyzer
+    ImportTableEntry *import;
+};
+
+struct AstNodeIfBoolExpr {
+    AstNode *condition;
+    AstNode *then_block;
+    AstNode *else_node; // null, block node, or other if expr node
+
+    // populated by semantic analyzer
+    Expr resolved_expr;
+};
+
+struct AstNodeIfVarExpr {
+    AstNodeVariableDeclaration var_decl;
+    AstNode *then_block;
+    AstNode *else_node; // null, block node, or other if expr node
+
+    // populated by semantic analyzer
+    TypeTableEntry *type;
+    BlockContext *block_context;
+    Expr resolved_expr;
+};
+
+struct AstNodeWhileExpr {
+    AstNode *condition;
+    AstNode *body;
+
+    // populated by semantic analyzer
+    bool condition_always_true;
+    bool contains_break;
+    Expr resolved_expr;
+};
+
+struct AstNodeLabel {
+    Buf name;
+
+    // populated by semantic analyzer
+    LabelTableEntry *label_entry;
+    Expr resolved_expr;
+};
+
+struct AstNodeGoto {
+    Buf name;
+
+    // populated by semantic analyzer
+    LabelTableEntry *label_entry;
+    Expr resolved_expr;
+};
+
+struct AsmOutput {
+    Buf asm_symbolic_name;
+    Buf constraint;
+    Buf variable_name;
+    AstNode *return_type; // null unless "=r" and return
+};
+
+struct AsmInput {
+    Buf asm_symbolic_name;
+    Buf constraint;
+    AstNode *expr;
+};
+
+struct SrcPos {
+    int line;
+    int column;
+};
+
+struct AstNodeAsmExpr {
+    bool is_volatile;
+    Buf asm_template;
+    ZigList<SrcPos> offset_map;
+    ZigList<AsmToken> token_list;
+    ZigList<AsmOutput*> output_list;
+    ZigList<AsmInput*> input_list;
+    ZigList<Buf*> clobber_list;
+
+    // populated by semantic analyzer
+    int return_count;
+    Expr resolved_expr;
+};
+
+struct AstNodeStructDecl {
+    Buf name;
+    ZigList<AstNode *> fields;
+    ZigList<AstNode *> fns;
+    ZigList<AstNode *> *directives;
+    VisibMod visib_mod;
+
+    // populated by semantic analyzer
+    TypeTableEntry *type_entry;
+    TopLevelDecl top_level_decl;
+};
+
+struct AstNodeStructField {
+    Buf name;
+    AstNode *type;
+    ZigList<AstNode *> *directives;
+};
+
+struct AstNodeEnumDecl {
+    Buf name;
+    ZigList<AstNode *> fields;
+    ZigList<AstNode *> *directives;
+    VisibMod visib_mod;
+};
+
+struct AstNodeEnumField {
+    Buf name;
+    ZigList<AstNode *> fields; // length 0 means simple enum
+    AstNode *val_expr;
+};
+
+struct AstNodeStringLiteral {
+    Buf buf;
+    bool c;
+
+    // populated by semantic analyzer:
+    Expr resolved_expr;
+};
+
+struct AstNodeCharLiteral {
+    uint8_t value;
+
+    // populated by semantic analyzer:
+    Expr resolved_expr;
+};
+
+enum NumLit {
+    NumLitF32,
+    NumLitF64,
+    NumLitF128,
+    NumLitU8,
+    NumLitU16,
+    NumLitU32,
+    NumLitU64,
+    NumLitI8,
+    NumLitI16,
+    NumLitI32,
+    NumLitI64,
+
+    NumLitCount
+};
+
+struct AstNodeNumberLiteral {
+    NumLit kind;
+
+    // overflow is true if when parsing the number, we discovered it would not
+    // fit without losing data in a uint64_t, int64_t, or double
+    bool overflow;
+
+    union {
+        uint64_t x_uint;
+        int64_t x_int;
+        double x_float;
+    } data;
+
+    // populated by semantic analyzer
+    NumLitCodeGen codegen;
+    Expr resolved_expr;
+};
+
+struct AstNodeStructValueField {
+    Buf name;
+    AstNode *expr;
+
+    // populated by semantic analyzer
+    int index;
+};
+
+struct AstNodeStructValueExpr {
+    AstNode *type;
+    ZigList<AstNode *> fields;
+
+    // populated by semantic analyzer
+    StructValExprCodeGen codegen;
+    Expr resolved_expr;
+};
+
+struct AstNodeCompilerFnExpr {
+    Buf name;
+    AstNode *expr;
+
+    // populated by semantic analyzer
+    Expr resolved_expr;
+};
+
+struct AstNodeCompilerFnType {
+    Buf name;
+    AstNode *type;
+
+    // populated by semantic analyzer
+    Expr resolved_expr;
+    NumLitCodeGen resolved_num_lit;
+};
+
+struct AstNodeNullLiteral {
+    // populated by semantic analyzer
+    StructValExprCodeGen resolved_struct_val_expr;
+    Expr resolved_expr;
+};
+
+struct AstNodeVoidExpr {
+    // populated by semantic analyzer
+    Expr resolved_expr;
+};
+
+struct AstNodeUnreachableExpr {
+    // populated by semantic analyzer
+    Expr resolved_expr;
+};
+
+struct AstNodeSymbolExpr {
+    Buf symbol;
+
+    // populated by semantic analyzer
+    Expr resolved_expr;
+};
+
+struct AstNodeBoolLiteral {
+    bool value;
+
+    // populated by semantic analyzer
+    Expr resolved_expr;
+};
+
+struct AstNodeBreakExpr {
+    // populated by semantic analyzer
+    Expr resolved_expr;
+};
+
+struct AstNodeContinueExpr {
+    // populated by semantic analyzer
+    Expr resolved_expr;
+};
+
+struct AstNode {
+    enum NodeType type;
+    int line;
+    int column;
+    uint32_t create_index; // for determinism purposes
+    ImportTableEntry *owner;
+    union {
+        AstNodeRoot root;
+        AstNodeRootExportDecl root_export_decl;
+        AstNodeFnDef fn_def;
+        AstNodeFnDecl fn_decl;
+        AstNodeFnProto fn_proto;
+        AstNodeType type;
+        AstNodeParamDecl param_decl;
+        AstNodeBlock block;
+        AstNodeReturnExpr return_expr;
+        AstNodeVariableDeclaration variable_declaration;
+        AstNodeBinOpExpr bin_op_expr;
+        AstNodeExternBlock extern_block;
+        AstNodeDirective directive;
+        AstNodeCastExpr cast_expr;
+        AstNodePrefixOpExpr prefix_op_expr;
+        AstNodeFnCallExpr fn_call_expr;
+        AstNodeArrayAccessExpr array_access_expr;
+        AstNodeSliceExpr slice_expr;
+        AstNodeUse use;
+        AstNodeIfBoolExpr if_bool_expr;
+        AstNodeIfVarExpr if_var_expr;
+        AstNodeWhileExpr while_expr;
+        AstNodeLabel label;
+        AstNodeGoto goto_expr;
+        AstNodeAsmExpr asm_expr;
+        AstNodeFieldAccessExpr field_access_expr;
+        AstNodeStructDecl struct_decl;
+        AstNodeStructField struct_field;
+        AstNodeEnumDecl enum_decl;
+        AstNodeEnumField enum_field;
+        AstNodeStringLiteral string_literal;
+        AstNodeCharLiteral char_literal;
+        AstNodeNumberLiteral number_literal;
+        AstNodeStructValueExpr struct_val_expr;
+        AstNodeStructValueField struct_val_field;
+        AstNodeCompilerFnExpr compiler_fn_expr;
+        AstNodeCompilerFnType compiler_fn_type;
+        AstNodeNullLiteral null_literal;
+        AstNodeVoidExpr void_expr;
+        AstNodeUnreachableExpr unreachable_expr;
+        AstNodeSymbolExpr symbol_expr;
+        AstNodeBoolLiteral bool_literal;
+        AstNodeBreakExpr break_expr;
+        AstNodeContinueExpr continue_expr;
+    } data;
+};
+
+enum AsmTokenId {
+    AsmTokenIdTemplate,
+    AsmTokenIdPercent,
+    AsmTokenIdVar,
+};
+
+struct AsmToken {
+    enum AsmTokenId id;
+    int start;
+    int end;
+};
+
+struct TypeTableEntryPointer {
+    TypeTableEntry *child_type;
+    bool is_const;
+    bool is_noalias;
+};
+
+struct TypeTableEntryInt {
+    bool is_signed;
+};
+
+struct TypeTableEntryArray {
+    TypeTableEntry *child_type;
+    uint64_t len;
+};
+
+struct TypeStructField {
+    Buf *name;
+    TypeTableEntry *type_entry;
+};
+
+struct TypeTableEntryStruct {
+    AstNode *decl_node;
+    bool is_packed;
+    int field_count;
+    TypeStructField *fields;
+    uint64_t size_bytes;
+    bool is_invalid; // true if any fields are invalid
+    bool is_unknown_size_array;
+    // reminder: hash tables must be initialized before use
+    HashMap<Buf *, FnTableEntry *, buf_hash, buf_eql_buf> fn_table;
+
+    // set this flag temporarily to detect infinite loops
+    bool embedded_in_current;
+    bool reported_infinite_err;
+};
+
+struct TypeTableEntryNumLit {
+    NumLit kind;
+};
+
+struct TypeTableEntryMaybe {
+    TypeTableEntry *child_type;
+};
+
+enum TypeTableEntryId {
+    TypeTableEntryIdInvalid,
+    TypeTableEntryIdVoid,
+    TypeTableEntryIdBool,
+    TypeTableEntryIdUnreachable,
+    TypeTableEntryIdInt,
+    TypeTableEntryIdFloat,
+    TypeTableEntryIdPointer,
+    TypeTableEntryIdArray,
+    TypeTableEntryIdStruct,
+    TypeTableEntryIdNumberLiteral,
+    TypeTableEntryIdMaybe,
+};
+
+struct TypeTableEntry {
+    TypeTableEntryId id;
+
+    LLVMTypeRef type_ref;
+    LLVMZigDIType *di_type;
+    uint64_t size_in_bits;
+    uint64_t align_in_bits;
+
+    Buf name;
+
+    union {
+        TypeTableEntryPointer pointer;
+        TypeTableEntryInt integral;
+        TypeTableEntryArray array;
+        TypeTableEntryStruct structure;
+        TypeTableEntryNumLit num_lit;
+        TypeTableEntryMaybe maybe;
+    } data;
+
+    // use these fields to make sure we don't duplicate type table entries for the same type
+    TypeTableEntry *pointer_parent[2][2]; // 0 - const. 1 - noalias
+    TypeTableEntry *unknown_size_array_parent[2][2]; // 0 - const. 1 - noalias
+    HashMap<uint64_t, TypeTableEntry *, uint64_hash, uint64_eq> arrays_by_size;
+    TypeTableEntry *maybe_parent;
+
+};
+
+struct ImporterInfo {
+    ImportTableEntry *import;
+    AstNode *source_node;
+};
+
+struct ImportTableEntry {
+    AstNode *root;
+    Buf *path; // relative to root_source_dir
+    LLVMZigDIFile *di_file;
+    Buf *source_code;
+    ZigList<int> *line_offsets;
+    BlockContext *block_context;
+    ZigList<ImporterInfo> importers;
+
+    // reminder: hash tables must be initialized before use
+    HashMap<Buf *, FnTableEntry *, buf_hash, buf_eql_buf> fn_table;
+    HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> type_table;
+};
+
+struct LabelTableEntry {
+    AstNode *label_node;
+    LLVMBasicBlockRef basic_block;
+    bool used;
+    bool entered_from_fallthrough;
+};
+
+enum FnAttrId {
+    FnAttrIdNaked,
+    FnAttrIdAlwaysInline,
+};
+
+struct FnTableEntry {
+    LLVMValueRef fn_value;
+    AstNode *proto_node;
+    AstNode *fn_def_node;
+    bool is_extern;
+    bool internal_linkage;
+    unsigned calling_convention;
+    ImportTableEntry *import_entry;
+    ZigList<FnAttrId> fn_attr_list;
+    // Required to be a pre-order traversal of the AST. (parents must come before children)
+    ZigList<BlockContext *> all_block_contexts;
+    TypeTableEntry *member_of_struct;
+    Buf symbol_name;
+
+    // reminder: hash tables must be initialized before use
+    HashMap<Buf *, LabelTableEntry *, buf_hash, buf_eql_buf> label_table;
+};
+
+enum BuiltinFnId {
+    BuiltinFnIdInvalid,
+    BuiltinFnIdArithmeticWithOverflow,
+    BuiltinFnIdMemcpy,
+    BuiltinFnIdMemset,
+};
+
+struct BuiltinFnEntry {
+    BuiltinFnId id;
+    Buf name;
+    int param_count;
+    TypeTableEntry *return_type;
+    TypeTableEntry **param_types;
+    LLVMValueRef fn_val;
+};
+
+struct CodeGen {
+    LLVMModuleRef module;
+    ZigList<ErrorMsg*> errors;
+    LLVMBuilderRef builder;
+    LLVMZigDIBuilder *dbuilder;
+    LLVMZigDICompileUnit *compile_unit;
+
+    ZigList<Buf *> lib_search_paths;
+
+    // reminder: hash tables must be initialized before use
+    HashMap<Buf *, LLVMValueRef, buf_hash, buf_eql_buf> str_table;
+    HashMap<Buf *, bool, buf_hash, buf_eql_buf> link_table;
+    HashMap<Buf *, ImportTableEntry *, buf_hash, buf_eql_buf> import_table;
+    HashMap<Buf *, BuiltinFnEntry *, buf_hash, buf_eql_buf> builtin_fn_table;
+    HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> primitive_type_table;
+    HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> unresolved_top_level_decls;
+
+    uint32_t next_unresolved_index;
+
+    struct {
+        TypeTableEntry *entry_bool;
+        TypeTableEntry *entry_u8;
+        TypeTableEntry *entry_u16;
+        TypeTableEntry *entry_u32;
+        TypeTableEntry *entry_u64;
+        TypeTableEntry *entry_i8;
+        TypeTableEntry *entry_i16;
+        TypeTableEntry *entry_i32;
+        TypeTableEntry *entry_i64;
+        TypeTableEntry *entry_isize;
+        TypeTableEntry *entry_usize;
+        TypeTableEntry *entry_f32;
+        TypeTableEntry *entry_f64;
+        TypeTableEntry *entry_c_string_literal;
+        TypeTableEntry *entry_void;
+        TypeTableEntry *entry_unreachable;
+        TypeTableEntry *entry_invalid;
+    } builtin_types;
+
+    TypeTableEntry *num_lit_types[NumLitCount];
+
+    LLVMTargetDataRef target_data_ref;
+    unsigned pointer_size_bytes;
+    bool is_static;
+    bool strip_debug_symbols;
+    bool have_exported_main;
+    bool link_libc;
+    Buf *libc_path;
+    CodeGenBuildType build_type;
+    LLVMTargetMachineRef target_machine;
+    LLVMZigDIFile *dummy_di_file;
+    bool is_native_target;
+    Buf *root_source_dir;
+    Buf *root_out_name;
+
+    // The function definitions this module includes. There must be a corresponding
+    // fn_protos entry.
+    ZigList<FnTableEntry *> fn_defs;
+    // The function prototypes this module includes. In the case of external declarations,
+    // there will not be a corresponding fn_defs entry.
+    ZigList<FnTableEntry *> fn_protos;
+    ZigList<VariableTableEntry *> global_vars;
+
+    OutType out_type;
+    FnTableEntry *cur_fn;
+    BlockContext *cur_block_context;
+    ZigList<LLVMBasicBlockRef> break_block_stack;
+    ZigList<LLVMBasicBlockRef> continue_block_stack;
+    bool c_stdint_used;
+    AstNode *root_export_decl;
+    int version_major;
+    int version_minor;
+    int version_patch;
+    bool verbose;
+    ErrColor err_color;
+    ImportTableEntry *root_import;
+    ImportTableEntry *bootstrap_import;
+    LLVMValueRef memcpy_fn_val;
+    bool error_during_imports;
+};
+
+struct VariableTableEntry {
+    Buf name;
+    TypeTableEntry *type;
+    LLVMValueRef value_ref;
+    bool is_const;
+    bool is_ptr; // if true, value_ref is a pointer
+    AstNode *decl_node;
+    LLVMZigDILocalVariable *di_loc_var;
+    int arg_index;
+};
+
+struct BlockContext {
+    AstNode *node; // either NodeTypeFnDef or NodeTypeBlock or NodeTypeRoot
+    FnTableEntry *fn_entry; // null at the module scope
+    BlockContext *parent; // null when this is the root
+    HashMap<Buf *, VariableTableEntry *, buf_hash, buf_eql_buf> variable_table;
+    ZigList<Cast *> cast_expr_alloca_list;
+    ZigList<StructValExprCodeGen *> struct_val_expr_alloca_list;
+    AstNode *parent_loop_node;
+    AstNode *next_child_parent_loop_node;
+    LLVMZigDIScope *di_scope;
+};
+
+#endif
src/analyze.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "analyze.hpp"
+#include "parser.hpp"
 #include "error.hpp"
 #include "zig_llvm.hpp"
 #include "os.hpp"
@@ -14,7 +15,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
         TypeTableEntry *expected_type, AstNode *node);
 static TypeTableEntry *eval_const_expr(CodeGen *g, BlockContext *context,
         AstNode *node, AstNodeNumberLiteral *out_number_literal);
-static void collect_type_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode *type_node, DeclNode *decl_node);
+static void collect_type_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode *type_node, TopLevelDecl *decl_node);
 static VariableTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableEntry *import,
         BlockContext *context, TypeTableEntry *expected_type, AstNode *node);
 
@@ -317,7 +318,7 @@ static TypeTableEntry *eval_const_expr_bin_op(CodeGen *g, BlockContext *context,
                     out_number_literal->kind = NumLitU8;
                     out_number_literal->overflow = false;
                     out_number_literal->data.x_uint = (op1_lit.data.x_uint != op2_lit.data.x_uint);
-                    return node->codegen_node->expr_node.type_entry;
+                    return get_resolved_expr(node)->type_entry;
                 } else {
                     return g->builtin_types.entry_invalid;
                 }
@@ -330,7 +331,7 @@ static TypeTableEntry *eval_const_expr_bin_op(CodeGen *g, BlockContext *context,
                     out_number_literal->kind = NumLitU8;
                     out_number_literal->overflow = false;
                     out_number_literal->data.x_uint = (op1_lit.data.x_uint < op2_lit.data.x_uint);
-                    return node->codegen_node->expr_node.type_entry;
+                    return get_resolved_expr(node)->type_entry;
                 } else {
                     return g->builtin_types.entry_invalid;
                 }
@@ -343,7 +344,7 @@ static TypeTableEntry *eval_const_expr_bin_op(CodeGen *g, BlockContext *context,
                     out_number_literal->kind = NumLitU64;
                     out_number_literal->overflow = false;
                     out_number_literal->data.x_uint = (op1_lit.data.x_uint % op2_lit.data.x_uint);
-                    return node->codegen_node->expr_node.type_entry;
+                    return get_resolved_expr(node)->type_entry;
                 } else {
                     return g->builtin_types.entry_invalid;
                 }
@@ -390,20 +391,20 @@ static TypeTableEntry *eval_const_expr(CodeGen *g, BlockContext *context,
     switch (node->type) {
         case NodeTypeNumberLiteral:
             *out_number_literal = node->data.number_literal;
-            return node->codegen_node->expr_node.type_entry;
+            return get_resolved_expr(node)->type_entry;
         case NodeTypeBoolLiteral:
-            out_number_literal->data.x_uint = node->data.bool_literal ? 1 : 0;
-            return node->codegen_node->expr_node.type_entry;
+            out_number_literal->data.x_uint = node->data.bool_literal.value ? 1 : 0;
+            return get_resolved_expr(node)->type_entry;
         case NodeTypeNullLiteral:
-            return node->codegen_node->expr_node.type_entry;
+            return get_resolved_expr(node)->type_entry;
         case NodeTypeBinOpExpr:
             return eval_const_expr_bin_op(g, context, node, out_number_literal);
         case NodeTypeCompilerFnType:
             {
                 Buf *name = &node->data.compiler_fn_type.name;
-                TypeTableEntry *expr_type = node->codegen_node->expr_node.type_entry;
+                TypeTableEntry *expr_type = get_resolved_expr(node)->type_entry;
                 if (buf_eql_str(name, "sizeof")) {
-                    TypeTableEntry *target_type = node->data.compiler_fn_type.type->codegen_node->data.type_node.entry;
+                    TypeTableEntry *target_type = node->data.compiler_fn_type.type->data.type.entry;
                     out_number_literal->overflow = false;
                     out_number_literal->data.x_uint = target_type->size_in_bits / 8;
                     out_number_literal->kind = get_number_literal_kind_unsigned(out_number_literal->data.x_uint);
@@ -420,11 +421,11 @@ static TypeTableEntry *eval_const_expr(CodeGen *g, BlockContext *context,
             }
         case NodeTypeSymbol:
             {
-                VariableTableEntry *var = find_variable(context, &node->data.symbol);
+                VariableTableEntry *var = find_variable(context, &node->data.symbol_expr.symbol);
                 assert(var);
                 AstNode *decl_node = var->decl_node;
                 AstNode *expr_node = decl_node->data.variable_declaration.expr;
-                BlockContext *next_context = expr_node->codegen_node->expr_node.block_context;
+                BlockContext *next_context = get_resolved_expr(expr_node)->block_context;
                 return eval_const_expr(g, next_context, expr_node, out_number_literal);
             }
         default:
@@ -436,8 +437,6 @@ static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node, ImportTableEntry
         BlockContext *context, bool noalias_allowed)
 {
     assert(node->type == NodeTypeType);
-    alloc_codegen_node(node);
-    TypeNode *type_node = &node->codegen_node->data.type_node;
     switch (node->data.type.type) {
         case AstNodeTypeTypePrimitive:
             {
@@ -447,13 +446,13 @@ static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node, ImportTableEntry
                     table_entry = g->primitive_type_table.maybe_get(name);
                 }
                 if (table_entry) {
-                    type_node->entry = table_entry->value;
+                    node->data.type.entry = table_entry->value;
                 } else {
                     add_node_error(g, node,
                             buf_sprintf("invalid type name: '%s'", buf_ptr(name)));
-                    type_node->entry = g->builtin_types.entry_invalid;
+                    node->data.type.entry = g->builtin_types.entry_invalid;
                 }
-                return type_node->entry;
+                return node->data.type.entry;
             }
         case AstNodeTypeTypePointer:
             {
@@ -468,19 +467,19 @@ static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node, ImportTableEntry
                 }
 
                 resolve_type(g, node->data.type.child_type, import, context, false);
-                TypeTableEntry *child_type = node->data.type.child_type->codegen_node->data.type_node.entry;
+                TypeTableEntry *child_type = node->data.type.child_type->data.type.entry;
                 assert(child_type);
                 if (child_type->id == TypeTableEntryIdUnreachable) {
                     add_node_error(g, node,
                             buf_create_from_str("pointer to unreachable not allowed"));
-                    type_node->entry = g->builtin_types.entry_invalid;
-                    return type_node->entry;
+                    node->data.type.entry = g->builtin_types.entry_invalid;
+                    return node->data.type.entry;
                 } else if (child_type->id == TypeTableEntryIdInvalid) {
-                    type_node->entry = child_type;
+                    node->data.type.entry = child_type;
                     return child_type;
                 } else {
-                    type_node->entry = get_pointer_to_type(g, child_type, node->data.type.is_const, use_noalias);
-                    return type_node->entry;
+                    node->data.type.entry = get_pointer_to_type(g, child_type, node->data.type.is_const, use_noalias);
+                    return node->data.type.entry;
                 }
             }
         case AstNodeTypeTypeArray:
@@ -501,16 +500,16 @@ static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node, ImportTableEntry
                 if (child_type->id == TypeTableEntryIdUnreachable) {
                     add_node_error(g, node,
                             buf_create_from_str("array of unreachable not allowed"));
-                    type_node->entry = g->builtin_types.entry_invalid;
-                    return type_node->entry;
+                    node->data.type.entry = g->builtin_types.entry_invalid;
+                    return node->data.type.entry;
                 }
 
                 if (size_node) {
                     TypeTableEntry *size_type = analyze_expression(g, import, context,
                             g->builtin_types.entry_usize, size_node);
                     if (size_type->id == TypeTableEntryIdInvalid) {
-                        type_node->entry = g->builtin_types.entry_invalid;
-                        return type_node->entry;
+                        node->data.type.entry = g->builtin_types.entry_invalid;
+                        return node->data.type.entry;
                     }
 
                     AstNodeNumberLiteral number_literal;
@@ -520,27 +519,27 @@ static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node, ImportTableEntry
                         if (resolved_type->data.integral.is_signed) {
                             add_node_error(g, size_node,
                                 buf_create_from_str("array size must be unsigned integer"));
-                            type_node->entry = g->builtin_types.entry_invalid;
+                            node->data.type.entry = g->builtin_types.entry_invalid;
                         } else {
-                            type_node->entry = get_array_type(g, import, child_type, number_literal.data.x_uint);
+                            node->data.type.entry = get_array_type(g, import, child_type, number_literal.data.x_uint);
                         }
                     } else {
                         add_node_error(g, size_node,
                             buf_create_from_str("unable to resolve constant expression"));
-                        type_node->entry = g->builtin_types.entry_invalid;
+                        node->data.type.entry = g->builtin_types.entry_invalid;
                     }
-                    return type_node->entry;
+                    return node->data.type.entry;
                 } else {
-                    type_node->entry = get_unknown_size_array_type(g, import, child_type,
+                    node->data.type.entry = get_unknown_size_array_type(g, import, child_type,
                             node->data.type.is_const, use_noalias);
-                    return type_node->entry;
+                    return node->data.type.entry;
                 }
 
             }
         case AstNodeTypeTypeMaybe:
             {
                 resolve_type(g, node->data.type.child_type, import, context, false);
-                TypeTableEntry *child_type = node->data.type.child_type->codegen_node->data.type_node.entry;
+                TypeTableEntry *child_type = node->data.type.child_type->data.type.entry;
                 assert(child_type);
                 if (child_type->id == TypeTableEntryIdUnreachable) {
                     add_node_error(g, node,
@@ -548,22 +547,22 @@ static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node, ImportTableEntry
                 } else if (child_type->id == TypeTableEntryIdInvalid) {
                     return child_type;
                 }
-                type_node->entry = get_maybe_type(g, import, child_type);
-                return type_node->entry;
+                node->data.type.entry = get_maybe_type(g, import, child_type);
+                return node->data.type.entry;
             }
         case AstNodeTypeTypeCompilerExpr:
             {
                 AstNode *compiler_expr_node = node->data.type.compiler_expr;
                 Buf *fn_name = &compiler_expr_node->data.compiler_fn_expr.name;
                 if (buf_eql_str(fn_name, "typeof")) {
-                    type_node->entry = analyze_expression(g, import, context, nullptr,
+                    node->data.type.entry = analyze_expression(g, import, context, nullptr,
                             compiler_expr_node->data.compiler_fn_expr.expr);
                 } else {
                     add_node_error(g, node,
                             buf_sprintf("invalid compiler function: '%s'", buf_ptr(fn_name)));
-                    type_node->entry = g->builtin_types.entry_invalid;
+                    node->data.type.entry = g->builtin_types.entry_invalid;
                 }
-                return type_node->entry;
+                return node->data.type.entry;
             }
     }
     zig_unreachable();
@@ -631,8 +630,7 @@ static void preview_function_labels(CodeGen *g, AstNode *node, FnTableEntry *fn_
         Buf *name = &label_node->data.label.name;
         fn_table_entry->label_table.put(name, label_entry);
 
-        alloc_codegen_node(label_node);
-        label_node->codegen_node->data.label_entry = label_entry;
+        label_node->data.label.label_entry = label_entry;
     }
 }
 
@@ -732,7 +730,8 @@ static void preview_fn_proto(CodeGen *g, ImportTableEntry *import,
     AstNode *struct_node = proto_node->data.fn_proto.struct_node;
     TypeTableEntry *struct_type;
     if (struct_node) {
-        struct_type = struct_node->codegen_node->data.struct_decl_node.type_entry;
+        assert(struct_node->type == NodeTypeStructDecl);
+        struct_type = struct_node->data.struct_decl.type_entry;
     } else {
         struct_type = nullptr;
     }
@@ -749,13 +748,14 @@ static void preview_fn_proto(CodeGen *g, ImportTableEntry *import,
     if (entry) {
         add_node_error(g, proto_node,
                 buf_sprintf("redefinition of '%s'", buf_ptr(proto_name)));
-        proto_node->codegen_node->data.fn_proto_node.skip = true;
+        proto_node->data.fn_proto.skip = true;
         skip = true;
     } else if (is_pub) {
+        // TODO is this else if branch a mistake?
         auto entry = fn_table->maybe_get(proto_name);
         if (entry) {
             add_node_error(g, proto_node, buf_sprintf("redefinition of '%s'", buf_ptr(proto_name)));
-            proto_node->codegen_node->data.fn_proto_node.skip = true;
+            proto_node->data.fn_proto.skip = true;
             skip = true;
         }
     }
@@ -804,7 +804,7 @@ static void preview_fn_proto(CodeGen *g, ImportTableEntry *import,
     resolve_function_proto(g, proto_node, fn_table_entry, import);
 
 
-    proto_node->codegen_node->data.fn_proto_node.fn_table_entry = fn_table_entry;
+    proto_node->data.fn_proto.fn_table_entry = fn_table_entry;
 
     if (fn_def_node) {
         preview_function_labels(g, fn_def_node->data.fn_def.body, fn_table_entry);
@@ -888,8 +888,7 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode
             break;
         case NodeTypeStructDecl:
             {
-                StructDeclNode *struct_codegen = &node->codegen_node->data.struct_decl_node;
-                TypeTableEntry *type_entry = struct_codegen->type_entry;
+                TypeTableEntry *type_entry = node->data.struct_decl.type_entry;
 
                 resolve_struct_type(g, import, type_entry);
 
@@ -962,8 +961,8 @@ static TypeTableEntry *get_return_type(BlockContext *context) {
     AstNode *fn_proto_node = fn_entry->proto_node;
     assert(fn_proto_node->type == NodeTypeFnProto);
     AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type;
-    assert(return_type_node->codegen_node);
-    return return_type_node->codegen_node->data.type_node.entry;
+    assert(return_type_node->type == NodeTypeType);
+    return return_type_node->data.type.entry;
 }
 
 static bool num_lit_fits_in_other_type(CodeGen *g, TypeTableEntry *literal_type, TypeTableEntry *other_type) {
@@ -1003,15 +1002,14 @@ static bool num_lit_fits_in_other_type(CodeGen *g, TypeTableEntry *literal_type,
     zig_unreachable();
 }
 
-static TypeTableEntry * resolve_rhs_number_literal(CodeGen *g, AstNode *non_literal_node,
+static TypeTableEntry *resolve_rhs_number_literal(CodeGen *g, AstNode *non_literal_node,
         TypeTableEntry *non_literal_type, AstNode *literal_node, TypeTableEntry *literal_type)
 {
-    assert(literal_node->codegen_node);
-    NumberLiteralNode *codegen_num_lit = &literal_node->codegen_node->data.num_lit_node;
+    NumLitCodeGen *num_lit_codegen = get_resolved_num_lit(literal_node);
 
     if (non_literal_type && num_lit_fits_in_other_type(g, literal_type, non_literal_type)) {
-        assert(!codegen_num_lit->resolved_type);
-        codegen_num_lit->resolved_type = non_literal_type;
+        assert(!num_lit_codegen->resolved_type);
+        num_lit_codegen->resolved_type = non_literal_type;
         return non_literal_type;
     } else {
         return nullptr;
@@ -1024,11 +1022,8 @@ static TypeTableEntry * resolve_number_literals(CodeGen *g, AstNode *node1, AstN
     if (type1->id == TypeTableEntryIdNumberLiteral &&
         type2->id == TypeTableEntryIdNumberLiteral)
     {
-        assert(node1->codegen_node);
-        assert(node2->codegen_node);
-
-        NumberLiteralNode *codegen_num_lit_1 = &node1->codegen_node->data.num_lit_node;
-        NumberLiteralNode *codegen_num_lit_2 = &node2->codegen_node->data.num_lit_node;
+        NumLitCodeGen *codegen_num_lit_1 = get_resolved_num_lit(node1);
+        NumLitCodeGen *codegen_num_lit_2 = get_resolved_num_lit(node2);
 
         assert(!codegen_num_lit_1->resolved_type);
         assert(!codegen_num_lit_2->resolved_type);
@@ -1113,9 +1108,10 @@ static TypeTableEntry *resolve_type_compatibility(CodeGen *g, BlockContext *cont
     if (actual_type->id == TypeTableEntryIdNumberLiteral &&
         num_lit_fits_in_other_type(g, actual_type, expected_type))
     {
-        assert(!node->codegen_node->data.num_lit_node.resolved_type ||
-                node->codegen_node->data.num_lit_node.resolved_type == expected_type);
-        node->codegen_node->data.num_lit_node.resolved_type = expected_type;
+        NumLitCodeGen *num_lit_code_gen = get_resolved_num_lit(node);
+        assert(!num_lit_code_gen->resolved_type ||
+                num_lit_code_gen->resolved_type == expected_type);
+        num_lit_code_gen->resolved_type = expected_type;
         return expected_type;
     }
 
@@ -1134,10 +1130,11 @@ static TypeTableEntry *resolve_type_compatibility(CodeGen *g, BlockContext *cont
         if (resolved_type->id == TypeTableEntryIdInvalid) {
             return resolved_type;
         }
-        node->codegen_node->expr_node.implicit_maybe_cast.op = CastOpMaybeWrap;
-        node->codegen_node->expr_node.implicit_maybe_cast.after_type = expected_type;
-        node->codegen_node->expr_node.implicit_maybe_cast.source_node = node;
-        context->cast_expr_alloca_list.append(&node->codegen_node->expr_node.implicit_maybe_cast);
+        Expr *expr = get_resolved_expr(node);
+        expr->implicit_maybe_cast.op = CastOpMaybeWrap;
+        expr->implicit_maybe_cast.after_type = expected_type;
+        expr->implicit_maybe_cast.source_node = node;
+        context->cast_expr_alloca_list.append(&expr->implicit_maybe_cast);
         return expected_type;
     }
 
@@ -1147,9 +1144,10 @@ static TypeTableEntry *resolve_type_compatibility(CodeGen *g, BlockContext *cont
         expected_type->data.integral.is_signed == actual_type->data.integral.is_signed &&
         expected_type->size_in_bits > actual_type->size_in_bits)
     {
-        node->codegen_node->expr_node.implicit_cast.after_type = expected_type;
-        node->codegen_node->expr_node.implicit_cast.op = CastOpIntWidenOrShorten;
-        node->codegen_node->expr_node.implicit_cast.source_node = node;
+        Expr *expr = get_resolved_expr(node);
+        expr->implicit_cast.after_type = expected_type;
+        expr->implicit_cast.op = CastOpIntWidenOrShorten;
+        expr->implicit_cast.source_node = node;
         return expected_type;
     }
 
@@ -1159,10 +1157,11 @@ static TypeTableEntry *resolve_type_compatibility(CodeGen *g, BlockContext *cont
         actual_type->id == TypeTableEntryIdArray &&
         actual_type->data.array.child_type == expected_type->data.structure.fields[0].type_entry->data.pointer.child_type)
     {
-        node->codegen_node->expr_node.implicit_cast.after_type = expected_type;
-        node->codegen_node->expr_node.implicit_cast.op = CastOpToUnknownSizeArray;
-        node->codegen_node->expr_node.implicit_cast.source_node = node;
-        context->cast_expr_alloca_list.append(&node->codegen_node->expr_node.implicit_cast);
+        Expr *expr = get_resolved_expr(node);
+        expr->implicit_cast.after_type = expected_type;
+        expr->implicit_cast.op = CastOpToUnknownSizeArray;
+        expr->implicit_cast.source_node = node;
+        context->cast_expr_alloca_list.append(&expr->implicit_cast);
         return expected_type;
     }
 
@@ -1225,7 +1224,7 @@ BlockContext *new_block_context(AstNode *node, BlockContext *parent) {
 
     if (node && node->type == NodeTypeFnDef) {
         AstNode *fn_proto_node = node->data.fn_def.fn_proto;
-        context->fn_entry = fn_proto_node->codegen_node->data.fn_proto_node.fn_table_entry;
+        context->fn_entry = fn_proto_node->data.fn_proto.fn_table_entry;
     } else if (parent) {
         context->fn_entry = parent->fn_entry;
     }
@@ -1275,6 +1274,8 @@ static void get_struct_field(TypeTableEntry *struct_type, Buf *name, TypeStructF
 static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         AstNode *node)
 {
+    assert(node->type == NodeTypeFieldAccessExpr);
+
     TypeTableEntry *struct_type = analyze_expression(g, import, context, nullptr,
             node->data.field_access_expr.struct_expr);
 
@@ -1283,20 +1284,16 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i
     if (struct_type->id == TypeTableEntryIdStruct || (struct_type->id == TypeTableEntryIdPointer &&
          struct_type->data.pointer.child_type->id == TypeTableEntryIdStruct))
     {
-        assert(node->codegen_node);
-        FieldAccessNode *codegen_field_access = &node->codegen_node->data.field_access_node;
-        assert(codegen_field_access);
-
         Buf *field_name = &node->data.field_access_expr.field_name;
 
         TypeTableEntry *bare_struct_type = (struct_type->id == TypeTableEntryIdStruct) ?
             struct_type : struct_type->data.pointer.child_type;
 
         get_struct_field(bare_struct_type, field_name,
-                &codegen_field_access->type_struct_field,
-                &codegen_field_access->field_index);
-        if (codegen_field_access->type_struct_field) {
-            return_type = codegen_field_access->type_struct_field->type_entry;
+                &node->data.field_access_expr.type_struct_field,
+                &node->data.field_access_expr.field_index);
+        if (node->data.field_access_expr.type_struct_field) {
+            return_type = node->data.field_access_expr.type_struct_field->type_entry;
         } else {
             add_node_error(g, node,
                 buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&struct_type->name)));
@@ -1329,6 +1326,8 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i
 static TypeTableEntry *analyze_slice_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         AstNode *node)
 {
+    assert(node->type == NodeTypeSliceExpr);
+
     TypeTableEntry *array_type = analyze_expression(g, import, context, nullptr,
             node->data.slice_expr.array_ref_expr);
 
@@ -1355,10 +1354,9 @@ static TypeTableEntry *analyze_slice_expr(CodeGen *g, ImportTableEntry *import,
     }
 
     if (return_type->id != TypeTableEntryIdInvalid) {
-        assert(node->codegen_node);
-        node->codegen_node->data.struct_val_expr_node.type_entry = return_type;
-        node->codegen_node->data.struct_val_expr_node.source_node = node;
-        context->struct_val_expr_alloca_list.append(&node->codegen_node->data.struct_val_expr_node);
+        node->data.slice_expr.resolved_struct_val_expr.type_entry = return_type;
+        node->data.slice_expr.resolved_struct_val_expr.source_node = node;
+        context->struct_val_expr_alloca_list.append(&node->data.slice_expr.resolved_struct_val_expr);
     }
 
     analyze_expression(g, import, context, g->builtin_types.entry_usize, node->data.slice_expr.start);
@@ -1463,6 +1461,8 @@ static bool is_op_allowed(TypeTableEntry *type, BinOpType op) {
 static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         TypeTableEntry *expected_type, AstNode *node)
 {
+    assert(node->type == NodeTypeCastExpr);
+
     TypeTableEntry *wanted_type = resolve_type(g, node->data.cast_expr.type, import, context, false);
     TypeTableEntry *actual_type = analyze_expression(g, import, context, nullptr, node->data.cast_expr.expr);
 
@@ -1472,43 +1472,41 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
         return g->builtin_types.entry_invalid;
     }
 
-    CastNode *cast_node = &node->codegen_node->data.cast_node;
-    cast_node->source_node = node;
-    cast_node->after_type = wanted_type;
+    Cast *cast = &node->data.cast_expr.cast;
+    cast->source_node = node;
+    cast->after_type = wanted_type;
 
-    // special casing this for now, TODO think about casting and do a general solution
     if ((wanted_type == g->builtin_types.entry_isize || wanted_type == g->builtin_types.entry_usize) &&
         actual_type->id == TypeTableEntryIdPointer)
     {
-        cast_node->op = CastOpPtrToInt;
+        cast->op = CastOpPtrToInt;
         return wanted_type;
     } else if (wanted_type->id == TypeTableEntryIdInt &&
                 actual_type->id == TypeTableEntryIdInt)
     {
-        cast_node->op = CastOpIntWidenOrShorten;
+        cast->op = CastOpIntWidenOrShorten;
         return wanted_type;
     } else if (wanted_type->id == TypeTableEntryIdStruct &&
                wanted_type->data.structure.is_unknown_size_array &&
                actual_type->id == TypeTableEntryIdArray &&
                actual_type->data.array.child_type == wanted_type->data.structure.fields[0].type_entry)
     {
-        cast_node->op = CastOpToUnknownSizeArray;
-        context->cast_expr_alloca_list.append(cast_node);
+        cast->op = CastOpToUnknownSizeArray;
+        context->cast_expr_alloca_list.append(cast);
         return wanted_type;
     } else if (actual_type->id == TypeTableEntryIdNumberLiteral &&
                num_lit_fits_in_other_type(g, actual_type, wanted_type))
     {
         AstNode *literal_node = node->data.cast_expr.expr;
-        assert(literal_node->codegen_node);
-        NumberLiteralNode *codegen_num_lit = &literal_node->codegen_node->data.num_lit_node;
+        NumLitCodeGen *codegen_num_lit = get_resolved_num_lit(literal_node);
         assert(!codegen_num_lit->resolved_type);
         codegen_num_lit->resolved_type = wanted_type;
-        cast_node->op = CastOpNothing;
+        cast->op = CastOpNothing;
         return wanted_type;
     } else if (actual_type->id == TypeTableEntryIdPointer &&
                wanted_type->id == TypeTableEntryIdPointer)
     {
-        cast_node->op = CastOpPointerReinterpret;
+        cast->op = CastOpPointerReinterpret;
         return wanted_type;
     } else {
         add_node_error(g, node,
@@ -1529,7 +1527,7 @@ static TypeTableEntry *analyze_lvalue(CodeGen *g, ImportTableEntry *import, Bloc
 {
     TypeTableEntry *expected_rhs_type = nullptr;
     if (lhs_node->type == NodeTypeSymbol) {
-        Buf *name = &lhs_node->data.symbol;
+        Buf *name = &lhs_node->data.symbol_expr.symbol;
         VariableTableEntry *var = find_variable(block_context, name);
         if (var) {
             if (purpose == LValPurposeAssign && var->is_const) {
@@ -1551,7 +1549,6 @@ static TypeTableEntry *analyze_lvalue(CodeGen *g, ImportTableEntry *import, Bloc
     } else if (lhs_node->type == NodeTypeArrayAccessExpr) {
         expected_rhs_type = analyze_array_access_expr(g, import, block_context, lhs_node);
     } else if (lhs_node->type == NodeTypeFieldAccessExpr) {
-        alloc_codegen_node(lhs_node);
         expected_rhs_type = analyze_field_access_expr(g, import, block_context, lhs_node);
     } else if (lhs_node->type == NodeTypePrefixOpExpr &&
             lhs_node->data.prefix_op_expr.prefix_op == PrefixOpDereference)
@@ -1774,10 +1771,9 @@ static TypeTableEntry *analyze_null_literal_expr(CodeGen *g, ImportTableEntry *i
     if (expected_type) {
         assert(expected_type->id == TypeTableEntryIdMaybe);
 
-        assert(node->codegen_node);
-        node->codegen_node->data.struct_val_expr_node.type_entry = expected_type;
-        node->codegen_node->data.struct_val_expr_node.source_node = node;
-        block_context->struct_val_expr_alloca_list.append(&node->codegen_node->data.struct_val_expr_node);
+        node->data.null_literal.resolved_struct_val_expr.type_entry = expected_type;
+        node->data.null_literal.resolved_struct_val_expr.source_node = node;
+        block_context->struct_val_expr_alloca_list.append(&node->data.null_literal.resolved_struct_val_expr);
 
         return expected_type;
     } else {
@@ -1796,7 +1792,7 @@ static TypeTableEntry *analyze_number_literal_expr(CodeGen *g, ImportTableEntry
                 buf_sprintf("number literal too large to be represented in any type"));
         return g->builtin_types.entry_invalid;
     } else if (expected_type) {
-        NumberLiteralNode *codegen_num_lit = &node->codegen_node->data.num_lit_node;
+        NumLitCodeGen *codegen_num_lit = get_resolved_num_lit(node);
         assert(!codegen_num_lit->resolved_type);
         TypeTableEntry *after_implicit_cast_resolved_type =
             resolve_type_compatibility(g, block_context, node, expected_type, num_lit_type);
@@ -1837,10 +1833,9 @@ static TypeTableEntry *analyze_struct_val_expr(CodeGen *g, ImportTableEntry *imp
         return g->builtin_types.entry_invalid;
     }
 
-    assert(node->codegen_node);
-    node->codegen_node->data.struct_val_expr_node.type_entry = type_entry;
-    node->codegen_node->data.struct_val_expr_node.source_node = node;
-    context->struct_val_expr_alloca_list.append(&node->codegen_node->data.struct_val_expr_node);
+    node->data.struct_val_expr.codegen.type_entry = type_entry;
+    node->data.struct_val_expr.codegen.source_node = node;
+    context->struct_val_expr_alloca_list.append(&node->data.struct_val_expr.codegen);
 
     int expr_field_count = struct_val_expr->fields.length;
     int actual_field_count = type_entry->data.structure.field_count;
@@ -1848,6 +1843,8 @@ static TypeTableEntry *analyze_struct_val_expr(CodeGen *g, ImportTableEntry *imp
     int *field_use_counts = allocate<int>(actual_field_count);
     for (int i = 0; i < expr_field_count; i += 1) {
         AstNode *val_field_node = struct_val_expr->fields.at(i);
+        assert(val_field_node->type == NodeTypeStructValueField);
+
         int field_index;
         TypeStructField *type_field = find_struct_type_field(type_entry,
                 &val_field_node->data.struct_val_field.name, &field_index);
@@ -1865,8 +1862,7 @@ static TypeTableEntry *analyze_struct_val_expr(CodeGen *g, ImportTableEntry *imp
             continue;
         }
 
-        alloc_codegen_node(val_field_node);
-        val_field_node->codegen_node->data.struct_val_field_node.index = field_index;
+        val_field_node->data.struct_val_field.index = field_index;
 
         analyze_expression(g, import, context, type_field->type_entry,
                 val_field_node->data.struct_val_field.expr);
@@ -1885,6 +1881,8 @@ static TypeTableEntry *analyze_struct_val_expr(CodeGen *g, ImportTableEntry *imp
 static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         TypeTableEntry *expected_type, AstNode *node)
 {
+    assert(node->type == NodeTypeWhileExpr);
+
     AstNode *condition_node = node->data.while_expr.condition;
     AstNode *while_body_node = node->data.while_expr.body;
     TypeTableEntry *condition_type = analyze_expression(g, import, context,
@@ -1907,8 +1905,8 @@ static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import,
             assert(resolved_type->id == TypeTableEntryIdBool);
             bool constant_cond_value = number_literal.data.x_uint;
             if (constant_cond_value) {
-                node->codegen_node->data.while_node.condition_always_true = true;
-                if (!node->codegen_node->data.while_node.contains_break) {
+                node->data.while_expr.condition_always_true = true;
+                if (!node->data.while_expr.contains_break) {
                     expr_return_type = g->builtin_types.entry_unreachable;
                 }
             }
@@ -1921,12 +1919,14 @@ static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import,
 static TypeTableEntry *analyze_break_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         TypeTableEntry *expected_type, AstNode *node)
 {
+    assert(node->type == NodeTypeBreak);
+
     AstNode *loop_node = context->parent_loop_node;
     if (loop_node) {
-        loop_node->codegen_node->data.while_node.contains_break = true;
+        assert(loop_node->type == NodeTypeWhileExpr);
+        loop_node->data.while_expr.contains_break = true;
     } else {
-        add_node_error(g, node,
-                buf_sprintf("'break' expression outside loop"));
+        add_node_error(g, node, buf_sprintf("'break' expression outside loop"));
     }
     return g->builtin_types.entry_unreachable;
 }
@@ -1935,8 +1935,7 @@ static TypeTableEntry *analyze_continue_expr(CodeGen *g, ImportTableEntry *impor
         TypeTableEntry *expected_type, AstNode *node)
 {
     if (!context->parent_loop_node) {
-        add_node_error(g, node,
-                buf_sprintf("'continue' expression outside loop"));
+        add_node_error(g, node, buf_sprintf("'continue' expression outside loop"));
     }
     return g->builtin_types.entry_unreachable;
 }
@@ -1981,7 +1980,7 @@ static TypeTableEntry *analyze_if_var_expr(CodeGen *g, ImportTableEntry *import,
     assert(node->type == NodeTypeIfVarExpr);
 
     BlockContext *child_context = new_block_context(node, context);
-    node->codegen_node->data.if_var_node.block_context = child_context;
+    node->data.if_var_expr.block_context = child_context;
 
     analyze_variable_declaration_raw(g, import, child_context, node, &node->data.if_var_expr.var_decl, true);
 
@@ -2032,8 +2031,10 @@ static TypeTableEntry *analyze_compiler_fn_type(CodeGen *g, ImportTableEntry *im
 static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         TypeTableEntry *expected_type, AstNode *node)
 {
+    assert(node->type == NodeTypeFnCallExpr);
+
     AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
-    Buf *name = &fn_ref_expr->data.symbol;
+    Buf *name = &fn_ref_expr->data.symbol_expr.symbol;
 
     auto entry = g->builtin_fn_table.maybe_get(name);
 
@@ -2041,8 +2042,7 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry
         BuiltinFnEntry *builtin_fn = entry->value;
         int actual_param_count = node->data.fn_call_expr.params.length;
 
-        assert(node->codegen_node);
-        node->codegen_node->data.fn_call_node.builtin_fn = builtin_fn;
+        node->data.fn_call_expr.builtin_fn = builtin_fn;
 
         if (builtin_fn->param_count != actual_param_count) {
             add_node_error(g, node,
@@ -2155,7 +2155,7 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import
         if (node->data.fn_call_expr.is_builtin) {
             return analyze_builtin_fn_call_expr(g, import, context, expected_type, node);
         }
-        name = &fn_ref_expr->data.symbol;
+        name = &fn_ref_expr->data.symbol_expr.symbol;
     } else {
         add_node_error(g, node,
                 buf_sprintf("function pointers not yet supported"));
@@ -2210,13 +2210,15 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import
                 AstNode *param_decl_node = fn_proto->params.at(fn_proto_i);
                 assert(param_decl_node->type == NodeTypeParamDecl);
                 AstNode *param_type_node = param_decl_node->data.param_decl.type;
-                if (param_type_node->codegen_node)
-                    expected_param_type = param_type_node->codegen_node->data.type_node.entry;
+                assert(param_type_node->type == NodeTypeType);
+                if (param_type_node->data.type.entry) {
+                    expected_param_type = param_type_node->data.type.entry;
+                }
             }
             analyze_expression(g, import, context, expected_param_type, child);
         }
 
-        return fn_proto->return_type->codegen_node->data.type_node.entry;
+        return fn_proto->return_type->data.type.entry;
     }
 }
 
@@ -2224,18 +2226,17 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
         TypeTableEntry *expected_type, AstNode *node)
 {
     TypeTableEntry *return_type = nullptr;
-    alloc_codegen_node(node);
     switch (node->type) {
         case NodeTypeBlock:
             {
                 BlockContext *child_context = new_block_context(node, context);
-                node->codegen_node->data.block_node.block_context = child_context;
+                node->data.block.block_context = child_context;
                 return_type = g->builtin_types.entry_void;
 
                 for (int i = 0; i < node->data.block.statements.length; i += 1) {
                     AstNode *child = node->data.block.statements.at(i);
                     if (child->type == NodeTypeLabel) {
-                        LabelTableEntry *label_entry = child->codegen_node->data.label_entry;
+                        LabelTableEntry *label_entry = child->data.label.label_entry;
                         assert(label_entry);
                         label_entry->entered_from_fallthrough = (return_type->id != TypeTableEntryIdUnreachable);
                         return_type = g->builtin_types.entry_void;
@@ -2288,13 +2289,13 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
         case NodeTypeGoto:
             {
                 FnTableEntry *fn_table_entry = get_context_fn_entry(context);
-                auto table_entry = fn_table_entry->label_table.maybe_get(&node->data.go_to.name);
+                auto table_entry = fn_table_entry->label_table.maybe_get(&node->data.goto_expr.name);
                 if (table_entry) {
-                    node->codegen_node->data.label_entry = table_entry->value;
+                    node->data.goto_expr.label_entry = table_entry->value;
                     table_entry->value->used = true;
                 } else {
                     add_node_error(g, node,
-                            buf_sprintf("use of undeclared label '%s'", buf_ptr(&node->data.go_to.name)));
+                            buf_sprintf("use of undeclared label '%s'", buf_ptr(&node->data.goto_expr.name)));
                 }
                 return_type = g->builtin_types.entry_unreachable;
                 break;
@@ -2380,7 +2381,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
 
         case NodeTypeSymbol:
             {
-                return_type = analyze_variable_name(g, import, context, node, &node->data.symbol);
+                return_type = analyze_variable_name(g, import, context, node, &node->data.symbol_expr.symbol);
                 break;
             }
         case NodeTypeCastExpr:
@@ -2507,8 +2508,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
     assert(return_type);
     resolve_type_compatibility(g, context, node, expected_type, return_type);
 
-    node->codegen_node->expr_node.type_entry = return_type;
-    node->codegen_node->expr_node.block_context = context;
+    get_resolved_expr(node)->type_entry = return_type;
+    get_resolved_expr(node)->block_context = context;
 
     return return_type;
 }
@@ -2519,15 +2520,14 @@ static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNo
     AstNode *fn_proto_node = node->data.fn_def.fn_proto;
     assert(fn_proto_node->type == NodeTypeFnProto);
 
-    if (fn_proto_node->codegen_node->data.fn_proto_node.skip) {
+    if (fn_proto_node->data.fn_proto.skip) {
         // we detected an error with this function definition which prevents us
         // from further analyzing it.
         return;
     }
 
-    alloc_codegen_node(node);
     BlockContext *context = new_block_context(node, import->block_context);
-    node->codegen_node->data.fn_def_node.block_context = context;
+    node->data.fn_def.block_context = context;
 
     AstNodeFnProto *fn_proto = &fn_proto_node->data.fn_proto;
     bool is_exported = (fn_proto->visib_mod == VisibModExport);
@@ -2538,7 +2538,7 @@ static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNo
         // define local variables for parameters
         AstNodeParamDecl *param_decl = &param_decl_node->data.param_decl;
         assert(param_decl->type->type == NodeTypeType);
-        TypeTableEntry *type = param_decl->type->codegen_node->data.type_node.entry;
+        TypeTableEntry *type = param_decl->type->data.type.entry;
 
         if (is_exported && type->id == TypeTableEntryIdStruct) {
             add_node_error(g, param_decl_node,
@@ -2552,8 +2552,7 @@ static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNo
         variable_entry->decl_node = param_decl_node;
         variable_entry->arg_index = i;
 
-        alloc_codegen_node(param_decl_node);
-        param_decl_node->codegen_node->data.param_decl_node.variable = variable_entry;
+        param_decl_node->data.param_decl.variable = variable_entry;
 
         VariableTableEntry *existing_entry = find_local_variable(context, &variable_entry->name);
         if (!existing_entry) {
@@ -2571,13 +2570,13 @@ static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNo
         }
     }
 
-    TypeTableEntry *expected_type = fn_proto->return_type->codegen_node->data.type_node.entry;
+    TypeTableEntry *expected_type = fn_proto->return_type->data.type.entry;
     TypeTableEntry *block_return_type = analyze_expression(g, import, context, expected_type, node->data.fn_def.body);
 
-    node->codegen_node->data.fn_def_node.implicit_return_type = block_return_type;
+    node->data.fn_def.implicit_return_type = block_return_type;
 
     {
-        FnTableEntry *fn_table_entry = fn_proto_node->codegen_node->data.fn_proto_node.fn_table_entry;
+        FnTableEntry *fn_table_entry = fn_proto_node->data.fn_proto.fn_table_entry;
         auto it = fn_table_entry->label_table.entry_iterator();
         for (;;) {
             auto *entry = it.next();
@@ -2656,7 +2655,7 @@ static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode
 }
 
 static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode *expr_node,
-        DeclNode *decl_node)
+        TopLevelDecl *decl_node)
 {
     switch (expr_node->type) {
         case NodeTypeNumberLiteral:
@@ -2672,7 +2671,7 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode
             // no dependencies on other top level declarations
             break;
         case NodeTypeSymbol:
-            decl_node->deps.put(&expr_node->data.symbol, expr_node);
+            decl_node->deps.put(&expr_node->data.symbol_expr.symbol, expr_node);
             break;
         case NodeTypeBinOpExpr:
             collect_expr_decl_deps(g, import, expr_node->data.bin_op_expr.op1, decl_node);
@@ -2787,7 +2786,7 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode
     }
 }
 
-static void collect_type_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode *type_node, DeclNode *decl_node) {
+static void collect_type_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode *type_node, TopLevelDecl *decl_node) {
     assert(type_node->type == NodeTypeType);
     switch (type_node->data.type.type) {
         case AstNodeTypeTypePrimitive:
@@ -2824,16 +2823,13 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast
     switch (node->type) {
         case NodeTypeStructDecl:
             {
-                alloc_codegen_node(node);
-                StructDeclNode *struct_codegen = &node->codegen_node->data.struct_decl_node;
-
                 Buf *name = &node->data.struct_decl.name;
                 auto table_entry = g->primitive_type_table.maybe_get(name);
                 if (!table_entry) {
                     table_entry = import->type_table.maybe_get(name);
                 }
                 if (table_entry) {
-                    struct_codegen->type_entry = table_entry->value;
+                    node->data.struct_decl.type_entry = table_entry->value;
                     add_node_error(g, node,
                             buf_sprintf("redefinition of '%s'", buf_ptr(name)));
                 } else {
@@ -2848,7 +2844,7 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast
                     // put off adding the debug type until we do the full struct body
                     // this type is incomplete until we do another pass
                     import->type_table.put(&entry->name, entry);
-                    struct_codegen->type_entry = entry;
+                    node->data.struct_decl.type_entry = entry;
 
                     bool is_pub = (node->data.struct_decl.visib_mod != VisibModPrivate);
                     if (is_pub) {
@@ -2867,14 +2863,15 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast
                 }
 
                 // determine which other top level declarations this struct depends on.
-                DeclNode *decl_node = &node->codegen_node->decl_node;
+                TopLevelDecl *decl_node = &node->data.struct_decl.top_level_decl;
+                decl_node->deps.init(1);
                 for (int i = 0; i < node->data.struct_decl.fields.length; i += 1) {
                     AstNode *field_node = node->data.struct_decl.fields.at(i);
                     AstNode *type_node = field_node->data.struct_field.type;
                     collect_type_decl_deps(g, import, type_node, decl_node);
                 }
-                node->codegen_node->decl_node.name = name;
-                node->codegen_node->decl_node.import = import;
+                decl_node->name = name;
+                decl_node->import = import;
                 if (decl_node->deps.size() > 0) {
                     g->unresolved_top_level_decls.put(name, node);
                 } else {
@@ -2913,8 +2910,8 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast
         case NodeTypeVariableDeclaration:
             {
                 // determine which other top level declarations this variable declaration depends on.
-                alloc_codegen_node(node);
-                DeclNode *decl_node = &node->codegen_node->decl_node;
+                TopLevelDecl *decl_node = &node->data.variable_declaration.top_level_decl;
+                decl_node->deps.init(1);
                 if (node->data.variable_declaration.type) {
                     collect_type_decl_deps(g, import, node->data.variable_declaration.type, decl_node);
                 }
@@ -2922,8 +2919,8 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast
                     collect_expr_decl_deps(g, import, node->data.variable_declaration.expr, decl_node);
                 }
                 Buf *name = &node->data.variable_declaration.symbol;
-                node->codegen_node->decl_node.name = name;
-                node->codegen_node->decl_node.import = import;
+                decl_node->name = name;
+                decl_node->import = import;
                 if (decl_node->deps.size() > 0) {
                     g->unresolved_top_level_decls.put(name, node);
                 } else {
@@ -2934,8 +2931,8 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast
         case NodeTypeFnProto:
             {
                 // determine which other top level declarations this function prototype depends on.
-                alloc_codegen_node(node);
-                DeclNode *decl_node = &node->codegen_node->decl_node;
+                TopLevelDecl *decl_node = &node->data.fn_proto.top_level_decl;
+                decl_node->deps.init(1);
                 for (int i = 0; i < node->data.fn_proto.params.length; i += 1) {
                     AstNode *param_node = node->data.fn_proto.params.at(i);
                     assert(param_node->type == NodeTypeParamDecl);
@@ -2943,8 +2940,8 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast
                 }
 
                 Buf *name = &node->data.fn_proto.name;
-                node->codegen_node->decl_node.name = name;
-                node->codegen_node->decl_node.import = import;
+                decl_node->name = name;
+                decl_node->import = import;
                 if (decl_node->deps.size() > 0) {
                     g->unresolved_top_level_decls.put(name, node);
                 } else {
@@ -2999,7 +2996,7 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast
 }
 
 static void recursive_resolve_decl(CodeGen *g, ImportTableEntry *import, AstNode *node) {
-    auto it = node->codegen_node->decl_node.deps.entry_iterator();
+    auto it = get_resolved_top_level_decl(node)->deps.entry_iterator();
     for (;;) {
         auto *entry = it.next();
         if (!entry)
@@ -3012,23 +3009,24 @@ static void recursive_resolve_decl(CodeGen *g, ImportTableEntry *import, AstNode
 
         AstNode *child_node = unresolved_entry->value;
 
-        if (child_node->codegen_node->decl_node.in_current_deps) {
+        if (get_resolved_top_level_decl(child_node)->in_current_deps) {
             // dependency loop. we'll let the fact that it's not in the respective
             // table cause an error in resolve_top_level_decl.
             continue;
         }
 
         // set temporary flag
-        child_node->codegen_node->decl_node.in_current_deps = true;
+        TopLevelDecl *top_level_decl = get_resolved_top_level_decl(child_node);
+        top_level_decl->in_current_deps = true;
 
-        recursive_resolve_decl(g, child_node->codegen_node->decl_node.import, child_node);
+        recursive_resolve_decl(g, top_level_decl->import, child_node);
 
         // unset temporary flag
-        child_node->codegen_node->decl_node.in_current_deps = false;
+        top_level_decl->in_current_deps = false;
     }
 
     resolve_top_level_decl(g, import, node);
-    g->unresolved_top_level_decls.remove(node->codegen_node->decl_node.name);
+    g->unresolved_top_level_decls.remove(get_resolved_top_level_decl(node)->name);
 }
 
 static void resolve_top_level_declarations_root(CodeGen *g, ImportTableEntry *import, AstNode *node) {
@@ -3051,12 +3049,13 @@ static void resolve_top_level_declarations_root(CodeGen *g, ImportTableEntry *im
 
         }
         // set temporary flag
-        decl_node->codegen_node->decl_node.in_current_deps = true;
+        TopLevelDecl *top_level_decl = get_resolved_top_level_decl(decl_node);
+        top_level_decl->in_current_deps = true;
 
-        recursive_resolve_decl(g, decl_node->codegen_node->decl_node.import, decl_node);
+        recursive_resolve_decl(g, top_level_decl->import, decl_node);
 
         // unset temporary flag
-        decl_node->codegen_node->decl_node.in_current_deps = false;
+        top_level_decl->in_current_deps = false;
     }
 }
 
@@ -3089,7 +3088,7 @@ void semantic_analyze(CodeGen *g) {
                                 buf_sprintf("invalid directive: '%s'", buf_ptr(name)));
                     }
 
-                    ImportTableEntry *target_import = child->codegen_node->data.import_node.import;
+                    ImportTableEntry *target_import = child->data.use.import;
                     assert(target_import);
 
                     target_import->importers.append({import, child});
@@ -3144,9 +3143,186 @@ void semantic_analyze(CodeGen *g) {
     }
 }
 
-void alloc_codegen_node(AstNode *node) {
-    assert(!node->codegen_node);
-    node->codegen_node = allocate<CodeGenNode>(1);
-    node->codegen_node->decl_node.deps.init(1);
+Expr *get_resolved_expr(AstNode *node) {
+    switch (node->type) {
+        case NodeTypeReturnExpr:
+            return &node->data.return_expr.resolved_expr;
+        case NodeTypeBinOpExpr:
+            return &node->data.bin_op_expr.resolved_expr;
+        case NodeTypeCastExpr:
+            return &node->data.cast_expr.resolved_expr;
+        case NodeTypePrefixOpExpr:
+            return &node->data.prefix_op_expr.resolved_expr;
+        case NodeTypeFnCallExpr:
+            return &node->data.fn_call_expr.resolved_expr;
+        case NodeTypeArrayAccessExpr:
+            return &node->data.array_access_expr.resolved_expr;
+        case NodeTypeSliceExpr:
+            return &node->data.slice_expr.resolved_expr;
+        case NodeTypeFieldAccessExpr:
+            return &node->data.field_access_expr.resolved_expr;
+        case NodeTypeIfBoolExpr:
+            return &node->data.if_bool_expr.resolved_expr;
+        case NodeTypeIfVarExpr:
+            return &node->data.if_var_expr.resolved_expr;
+        case NodeTypeWhileExpr:
+            return &node->data.while_expr.resolved_expr;
+        case NodeTypeAsmExpr:
+            return &node->data.asm_expr.resolved_expr;
+        case NodeTypeStructValueExpr:
+            return &node->data.struct_val_expr.resolved_expr;
+        case NodeTypeNumberLiteral:
+            return &node->data.number_literal.resolved_expr;
+        case NodeTypeStringLiteral:
+            return &node->data.string_literal.resolved_expr;
+        case NodeTypeBlock:
+            return &node->data.block.resolved_expr;
+        case NodeTypeVoid:
+            return &node->data.void_expr.resolved_expr;
+        case NodeTypeUnreachable:
+            return &node->data.unreachable_expr.resolved_expr;
+        case NodeTypeSymbol:
+            return &node->data.symbol_expr.resolved_expr;
+        case NodeTypeVariableDeclaration:
+            return &node->data.variable_declaration.resolved_expr;
+        case NodeTypeCharLiteral:
+            return &node->data.char_literal.resolved_expr;
+        case NodeTypeBoolLiteral:
+            return &node->data.bool_literal.resolved_expr;
+        case NodeTypeNullLiteral:
+            return &node->data.null_literal.resolved_expr;
+        case NodeTypeGoto:
+            return &node->data.goto_expr.resolved_expr;
+        case NodeTypeBreak:
+            return &node->data.break_expr.resolved_expr;
+        case NodeTypeContinue:
+            return &node->data.continue_expr.resolved_expr;
+        case NodeTypeCompilerFnExpr:
+            return &node->data.compiler_fn_expr.resolved_expr;
+        case NodeTypeCompilerFnType:
+            return &node->data.compiler_fn_type.resolved_expr;
+        case NodeTypeLabel:
+            return &node->data.label.resolved_expr;
+        case NodeTypeRoot:
+        case NodeTypeRootExportDecl:
+        case NodeTypeFnProto:
+        case NodeTypeFnDef:
+        case NodeTypeFnDecl:
+        case NodeTypeParamDecl:
+        case NodeTypeType:
+        case NodeTypeExternBlock:
+        case NodeTypeDirective:
+        case NodeTypeUse:
+        case NodeTypeStructDecl:
+        case NodeTypeStructField:
+        case NodeTypeStructValueField:
+        case NodeTypeEnumDecl:
+        case NodeTypeEnumField:
+            zig_unreachable();
+    }
+}
+
+NumLitCodeGen *get_resolved_num_lit(AstNode *node) {
+    switch (node->type) {
+        case NodeTypeNumberLiteral:
+            return &node->data.number_literal.codegen;
+        case NodeTypeCompilerFnType:
+            return &node->data.compiler_fn_type.resolved_num_lit;
+        case NodeTypeReturnExpr:
+        case NodeTypeBinOpExpr:
+        case NodeTypeCastExpr:
+        case NodeTypePrefixOpExpr:
+        case NodeTypeFnCallExpr:
+        case NodeTypeArrayAccessExpr:
+        case NodeTypeSliceExpr:
+        case NodeTypeFieldAccessExpr:
+        case NodeTypeIfBoolExpr:
+        case NodeTypeIfVarExpr:
+        case NodeTypeWhileExpr:
+        case NodeTypeAsmExpr:
+        case NodeTypeStructValueExpr:
+        case NodeTypeRoot:
+        case NodeTypeRootExportDecl:
+        case NodeTypeFnProto:
+        case NodeTypeFnDef:
+        case NodeTypeFnDecl:
+        case NodeTypeParamDecl:
+        case NodeTypeType:
+        case NodeTypeBlock:
+        case NodeTypeExternBlock:
+        case NodeTypeDirective:
+        case NodeTypeVariableDeclaration:
+        case NodeTypeStringLiteral:
+        case NodeTypeCharLiteral:
+        case NodeTypeUnreachable:
+        case NodeTypeSymbol:
+        case NodeTypeUse:
+        case NodeTypeVoid:
+        case NodeTypeBoolLiteral:
+        case NodeTypeNullLiteral:
+        case NodeTypeLabel:
+        case NodeTypeGoto:
+        case NodeTypeBreak:
+        case NodeTypeContinue:
+        case NodeTypeStructDecl:
+        case NodeTypeStructField:
+        case NodeTypeStructValueField:
+        case NodeTypeEnumDecl:
+        case NodeTypeEnumField:
+        case NodeTypeCompilerFnExpr:
+            zig_unreachable();
+    }
 }
 
+TopLevelDecl *get_resolved_top_level_decl(AstNode *node) {
+    switch (node->type) {
+        case NodeTypeVariableDeclaration:
+            return &node->data.variable_declaration.top_level_decl;
+        case NodeTypeFnProto:
+            return &node->data.fn_proto.top_level_decl;
+        case NodeTypeStructDecl:
+            return &node->data.struct_decl.top_level_decl;
+        case NodeTypeNumberLiteral:
+        case NodeTypeReturnExpr:
+        case NodeTypeBinOpExpr:
+        case NodeTypeCastExpr:
+        case NodeTypePrefixOpExpr:
+        case NodeTypeFnCallExpr:
+        case NodeTypeArrayAccessExpr:
+        case NodeTypeSliceExpr:
+        case NodeTypeFieldAccessExpr:
+        case NodeTypeIfBoolExpr:
+        case NodeTypeIfVarExpr:
+        case NodeTypeWhileExpr:
+        case NodeTypeAsmExpr:
+        case NodeTypeStructValueExpr:
+        case NodeTypeRoot:
+        case NodeTypeRootExportDecl:
+        case NodeTypeFnDef:
+        case NodeTypeFnDecl:
+        case NodeTypeParamDecl:
+        case NodeTypeType:
+        case NodeTypeBlock:
+        case NodeTypeExternBlock:
+        case NodeTypeDirective:
+        case NodeTypeStringLiteral:
+        case NodeTypeCharLiteral:
+        case NodeTypeUnreachable:
+        case NodeTypeSymbol:
+        case NodeTypeUse:
+        case NodeTypeVoid:
+        case NodeTypeBoolLiteral:
+        case NodeTypeNullLiteral:
+        case NodeTypeLabel:
+        case NodeTypeGoto:
+        case NodeTypeBreak:
+        case NodeTypeContinue:
+        case NodeTypeStructField:
+        case NodeTypeStructValueField:
+        case NodeTypeEnumDecl:
+        case NodeTypeEnumField:
+        case NodeTypeCompilerFnExpr:
+        case NodeTypeCompilerFnType:
+            zig_unreachable();
+    }
+}
src/analyze.hpp
@@ -8,414 +8,16 @@
 #ifndef ZIG_ANALYZE_HPP
 #define ZIG_ANALYZE_HPP
 
-#include "codegen.hpp"
-#include "hash_map.hpp"
-#include "zig_llvm.hpp"
-#include "errmsg.hpp"
-
-struct FnTableEntry;
-struct BlockContext;
-struct TypeTableEntry;
-struct VariableTableEntry;
-struct CastNode;
-struct StructValExprNode;
-
-struct TypeTableEntryPointer {
-    TypeTableEntry *child_type;
-    bool is_const;
-    bool is_noalias;
-};
-
-struct TypeTableEntryInt {
-    bool is_signed;
-};
-
-struct TypeTableEntryArray {
-    TypeTableEntry *child_type;
-    uint64_t len;
-};
-
-struct TypeStructField {
-    Buf *name;
-    TypeTableEntry *type_entry;
-};
-
-struct TypeTableEntryStruct {
-    AstNode *decl_node;
-    bool is_packed;
-    int field_count;
-    TypeStructField *fields;
-    uint64_t size_bytes;
-    bool is_invalid; // true if any fields are invalid
-    bool is_unknown_size_array;
-    // reminder: hash tables must be initialized before use
-    HashMap<Buf *, FnTableEntry *, buf_hash, buf_eql_buf> fn_table;
-
-    // set this flag temporarily to detect infinite loops
-    bool embedded_in_current;
-    bool reported_infinite_err;
-};
-
-struct TypeTableEntryNumLit {
-    NumLit kind;
-};
-
-struct TypeTableEntryMaybe {
-    TypeTableEntry *child_type;
-};
-
-enum TypeTableEntryId {
-    TypeTableEntryIdInvalid,
-    TypeTableEntryIdVoid,
-    TypeTableEntryIdBool,
-    TypeTableEntryIdUnreachable,
-    TypeTableEntryIdInt,
-    TypeTableEntryIdFloat,
-    TypeTableEntryIdPointer,
-    TypeTableEntryIdArray,
-    TypeTableEntryIdStruct,
-    TypeTableEntryIdNumberLiteral,
-    TypeTableEntryIdMaybe,
-};
-
-struct TypeTableEntry {
-    TypeTableEntryId id;
-
-    LLVMTypeRef type_ref;
-    LLVMZigDIType *di_type;
-    uint64_t size_in_bits;
-    uint64_t align_in_bits;
-
-    Buf name;
-
-    union {
-        TypeTableEntryPointer pointer;
-        TypeTableEntryInt integral;
-        TypeTableEntryArray array;
-        TypeTableEntryStruct structure;
-        TypeTableEntryNumLit num_lit;
-        TypeTableEntryMaybe maybe;
-    } data;
-
-    // use these fields to make sure we don't duplicate type table entries for the same type
-    TypeTableEntry *pointer_parent[2][2]; // 0 - const. 1 - noalias
-    TypeTableEntry *unknown_size_array_parent[2][2]; // 0 - const. 1 - noalias
-    HashMap<uint64_t, TypeTableEntry *, uint64_hash, uint64_eq> arrays_by_size;
-    TypeTableEntry *maybe_parent;
-
-};
-
-struct ImporterInfo {
-    ImportTableEntry *import;
-    AstNode *source_node;
-};
-
-struct ImportTableEntry {
-    AstNode *root;
-    Buf *path; // relative to root_source_dir
-    LLVMZigDIFile *di_file;
-    Buf *source_code;
-    ZigList<int> *line_offsets;
-    BlockContext *block_context;
-    ZigList<ImporterInfo> importers;
-
-    // reminder: hash tables must be initialized before use
-    HashMap<Buf *, FnTableEntry *, buf_hash, buf_eql_buf> fn_table;
-    HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> type_table;
-};
-
-struct LabelTableEntry {
-    AstNode *label_node;
-    LLVMBasicBlockRef basic_block;
-    bool used;
-    bool entered_from_fallthrough;
-};
-
-enum FnAttrId {
-    FnAttrIdNaked,
-    FnAttrIdAlwaysInline,
-};
-
-struct FnTableEntry {
-    LLVMValueRef fn_value;
-    AstNode *proto_node;
-    AstNode *fn_def_node;
-    bool is_extern;
-    bool internal_linkage;
-    unsigned calling_convention;
-    ImportTableEntry *import_entry;
-    ZigList<FnAttrId> fn_attr_list;
-    // Required to be a pre-order traversal of the AST. (parents must come before children)
-    ZigList<BlockContext *> all_block_contexts;
-    TypeTableEntry *member_of_struct;
-    Buf symbol_name;
-
-    // reminder: hash tables must be initialized before use
-    HashMap<Buf *, LabelTableEntry *, buf_hash, buf_eql_buf> label_table;
-};
-
-enum BuiltinFnId {
-    BuiltinFnIdInvalid,
-    BuiltinFnIdArithmeticWithOverflow,
-    BuiltinFnIdMemcpy,
-    BuiltinFnIdMemset,
-};
-
-struct BuiltinFnEntry {
-    BuiltinFnId id;
-    Buf name;
-    int param_count;
-    TypeTableEntry *return_type;
-    TypeTableEntry **param_types;
-    LLVMValueRef fn_val;
-};
-
-struct CodeGen {
-    LLVMModuleRef module;
-    ZigList<ErrorMsg*> errors;
-    LLVMBuilderRef builder;
-    LLVMZigDIBuilder *dbuilder;
-    LLVMZigDICompileUnit *compile_unit;
-
-    ZigList<Buf *> lib_search_paths;
-
-    // reminder: hash tables must be initialized before use
-    HashMap<Buf *, LLVMValueRef, buf_hash, buf_eql_buf> str_table;
-    HashMap<Buf *, bool, buf_hash, buf_eql_buf> link_table;
-    HashMap<Buf *, ImportTableEntry *, buf_hash, buf_eql_buf> import_table;
-    HashMap<Buf *, BuiltinFnEntry *, buf_hash, buf_eql_buf> builtin_fn_table;
-    HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> primitive_type_table;
-    HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> unresolved_top_level_decls;
-
-    uint32_t next_unresolved_index;
-
-    struct {
-        TypeTableEntry *entry_bool;
-        TypeTableEntry *entry_u8;
-        TypeTableEntry *entry_u16;
-        TypeTableEntry *entry_u32;
-        TypeTableEntry *entry_u64;
-        TypeTableEntry *entry_i8;
-        TypeTableEntry *entry_i16;
-        TypeTableEntry *entry_i32;
-        TypeTableEntry *entry_i64;
-        TypeTableEntry *entry_isize;
-        TypeTableEntry *entry_usize;
-        TypeTableEntry *entry_f32;
-        TypeTableEntry *entry_f64;
-        TypeTableEntry *entry_c_string_literal;
-        TypeTableEntry *entry_void;
-        TypeTableEntry *entry_unreachable;
-        TypeTableEntry *entry_invalid;
-    } builtin_types;
-
-    TypeTableEntry *num_lit_types[NumLitCount];
-
-    LLVMTargetDataRef target_data_ref;
-    unsigned pointer_size_bytes;
-    bool is_static;
-    bool strip_debug_symbols;
-    bool have_exported_main;
-    bool link_libc;
-    Buf *libc_path;
-    CodeGenBuildType build_type;
-    LLVMTargetMachineRef target_machine;
-    LLVMZigDIFile *dummy_di_file;
-    bool is_native_target;
-    Buf *root_source_dir;
-    Buf *root_out_name;
-
-    // The function definitions this module includes. There must be a corresponding
-    // fn_protos entry.
-    ZigList<FnTableEntry *> fn_defs;
-    // The function prototypes this module includes. In the case of external declarations,
-    // there will not be a corresponding fn_defs entry.
-    ZigList<FnTableEntry *> fn_protos;
-    ZigList<VariableTableEntry *> global_vars;
-
-    OutType out_type;
-    FnTableEntry *cur_fn;
-    BlockContext *cur_block_context;
-    ZigList<LLVMBasicBlockRef> break_block_stack;
-    ZigList<LLVMBasicBlockRef> continue_block_stack;
-    bool c_stdint_used;
-    AstNode *root_export_decl;
-    int version_major;
-    int version_minor;
-    int version_patch;
-    bool verbose;
-    ErrColor err_color;
-    ImportTableEntry *root_import;
-    ImportTableEntry *bootstrap_import;
-    LLVMValueRef memcpy_fn_val;
-    bool error_during_imports;
-};
-
-struct VariableTableEntry {
-    Buf name;
-    TypeTableEntry *type;
-    LLVMValueRef value_ref;
-    bool is_const;
-    bool is_ptr; // if true, value_ref is a pointer
-    AstNode *decl_node;
-    LLVMZigDILocalVariable *di_loc_var;
-    int arg_index;
-};
-
-struct BlockContext {
-    AstNode *node; // either NodeTypeFnDef or NodeTypeBlock or NodeTypeRoot
-    FnTableEntry *fn_entry; // null at the module scope
-    BlockContext *parent; // null when this is the root
-    HashMap<Buf *, VariableTableEntry *, buf_hash, buf_eql_buf> variable_table;
-    ZigList<CastNode *> cast_expr_alloca_list;
-    ZigList<StructValExprNode *> struct_val_expr_alloca_list;
-    AstNode *parent_loop_node;
-    AstNode *next_child_parent_loop_node;
-    LLVMZigDIScope *di_scope;
-};
-
-struct TypeNode {
-    TypeTableEntry *entry;
-};
-
-struct FnProtoNode {
-    FnTableEntry *fn_table_entry;
-    bool skip;
-};
-
-struct FnDefNode {
-    TypeTableEntry *implicit_return_type;
-    BlockContext *block_context;
-};
-
-
-struct AssignNode {
-    VariableTableEntry *var_entry;
-};
-
-struct BlockNode {
-    BlockContext *block_context;
-};
-
-struct StructDeclNode {
-    TypeTableEntry *type_entry;
-};
-
-struct FieldAccessNode {
-    int field_index;
-    TypeStructField *type_struct_field;
-};
-
-enum CastOp {
-    CastOpNothing,
-    CastOpPtrToInt,
-    CastOpIntWidenOrShorten,
-    CastOpToUnknownSizeArray,
-    CastOpMaybeWrap,
-    CastOpPointerReinterpret,
-};
-
-struct CastNode {
-    CastOp op;
-    // if op is CastOpArrayToString, this will be a pointer to
-    // the string struct on the stack
-    LLVMValueRef ptr;
-    TypeTableEntry *after_type;
-    AstNode *source_node;
-};
-
-struct ExprNode {
-    TypeTableEntry *type_entry;
-    // the context in which this expression is evaluated.
-    // for blocks, this points to the containing scope, not the block's own scope for its children.
-    BlockContext *block_context;
-
-    // may be null for no cast
-    CastNode implicit_cast; // happens first
-    CastNode implicit_maybe_cast; // happens second
-};
-
-struct NumberLiteralNode {
-    TypeTableEntry *resolved_type;
-};
-
-struct VarDeclNode {
-    TypeTableEntry *type;
-};
-
-struct StructValFieldNode {
-    int index;
-};
-
-struct StructValExprNode {
-    TypeTableEntry *type_entry;
-    LLVMValueRef ptr;
-    AstNode *source_node;
-};
-
-struct IfVarNode {
-    BlockContext *block_context;
-};
-
-struct ParamDeclNode {
-    VariableTableEntry *variable;
-};
-
-struct ImportNode {
-    ImportTableEntry *import;
-};
-
-struct WhileNode {
-    bool condition_always_true;
-    bool contains_break;
-};
-
-struct FnCallNode {
-    BuiltinFnEntry *builtin_fn;
-};
-
-struct DeclNode {
-    HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> deps;
-    Buf *name;
-    ImportTableEntry *import;
-    // set this flag temporarily to detect infinite loops
-    bool in_current_deps;
-};
-
-// TODO get rid of this structure and put the data directly in the appropriate AST node
-struct CodeGenNode {
-    union {
-        TypeNode type_node; // for NodeTypeType
-        FnDefNode fn_def_node; // for NodeTypeFnDef
-        FnProtoNode fn_proto_node; // for NodeTypeFnProto
-        LabelTableEntry *label_entry; // for NodeTypeGoto and NodeTypeLabel
-        AssignNode assign_node; // for NodeTypeBinOpExpr where op is BinOpTypeAssign
-        BlockNode block_node; // for NodeTypeBlock
-        StructDeclNode struct_decl_node; // for NodeTypeStructDecl
-        FieldAccessNode field_access_node; // for NodeTypeFieldAccessExpr
-        CastNode cast_node; // for NodeTypeCastExpr
-        // note: I've been using this field on some non-number literal nodes too.
-        NumberLiteralNode num_lit_node; // for NodeTypeNumberLiteral
-        VarDeclNode var_decl_node; // for NodeTypeVariableDeclaration
-        StructValFieldNode struct_val_field_node; // for NodeTypeStructValueField
-        // note: I've been using this field on some non-struct val expressions too.
-        StructValExprNode struct_val_expr_node; // for NodeTypeStructValueExpr
-        IfVarNode if_var_node; // for NodeTypeStructValueExpr
-        ParamDeclNode param_decl_node; // for NodeTypeParamDecl
-        ImportNode import_node; // for NodeTypeUse
-        WhileNode while_node; // for NodeTypeWhileExpr
-        FnCallNode fn_call_node; // for NodeTypeFnCallExpr
-    } data;
-    ExprNode expr_node; // for all the expression nodes
-    DeclNode decl_node; // for all top level decls
-};
+#include "all_types.hpp"
 
 void semantic_analyze(CodeGen *g);
 void add_node_error(CodeGen *g, AstNode *node, Buf *msg);
-void alloc_codegen_node(AstNode *node);
 TypeTableEntry *new_type_table_entry(TypeTableEntryId id);
 TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const, bool is_noalias);
 VariableTableEntry *find_variable(BlockContext *context, Buf *name);
 BlockContext *new_block_context(AstNode *node, BlockContext *parent);
+Expr *get_resolved_expr(AstNode *node);
+NumLitCodeGen *get_resolved_num_lit(AstNode *node);
+TopLevelDecl *get_resolved_top_level_decl(AstNode *node);
 
 #endif
src/codegen.cpp
@@ -75,9 +75,7 @@ static LLVMValueRef gen_assign_raw(CodeGen *g, AstNode *source_node, BinOpType b
 
 static TypeTableEntry *get_type_for_type_node(CodeGen *g, AstNode *type_node) {
     assert(type_node->type == NodeTypeType);
-    assert(type_node->codegen_node);
-    assert(type_node->codegen_node->data.type_node.entry);
-    return type_node->codegen_node->data.type_node.entry;
+    return type_node->data.type.entry;
 }
 
 static TypeTableEntry *fn_proto_type_from_type_node(CodeGen *g, AstNode *type_node) {
@@ -138,15 +136,16 @@ static LLVMValueRef find_or_create_string(CodeGen *g, Buf *str, bool c) {
 }
 
 static TypeTableEntry *get_expr_type(AstNode *node) {
-    TypeTableEntry *cast_type = node->codegen_node->expr_node.implicit_cast.after_type;
-    return cast_type ? cast_type : node->codegen_node->expr_node.type_entry;
+    Expr *expr = get_resolved_expr(node);
+    TypeTableEntry *cast_type = expr->implicit_cast.after_type;
+    return cast_type ? cast_type : expr->type_entry;
 }
 
 static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
     assert(node->type == NodeTypeFnCallExpr);
     AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
     assert(fn_ref_expr->type == NodeTypeSymbol);
-    BuiltinFnEntry *builtin_fn = node->codegen_node->data.fn_call_node.builtin_fn;
+    BuiltinFnEntry *builtin_fn = node->data.fn_call_expr.builtin_fn;
 
     switch (builtin_fn->id) {
         case BuiltinFnIdInvalid:
@@ -265,7 +264,7 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {
         // Assume that the expression evaluates to a simple name and return the buf
         // TODO after we support function pointers we can make this generic
         assert(fn_ref_expr->type == NodeTypeSymbol);
-        Buf *name = &fn_ref_expr->data.symbol;
+        Buf *name = &fn_ref_expr->data.symbol_expr.symbol;
 
         struct_type = nullptr;
         first_param_expr = nullptr;
@@ -388,8 +387,8 @@ static LLVMValueRef gen_field_ptr(CodeGen *g, AstNode *node, TypeTableEntry **ou
 
     LLVMValueRef struct_ptr;
     if (struct_expr_node->type == NodeTypeSymbol) {
-        VariableTableEntry *var = find_variable(struct_expr_node->codegen_node->expr_node.block_context,
-                &struct_expr_node->data.symbol);
+        VariableTableEntry *var = find_variable(get_resolved_expr(struct_expr_node)->block_context,
+                &struct_expr_node->data.symbol_expr.symbol);
         assert(var);
 
         if (var->is_ptr && var->type->id == TypeTableEntryIdPointer) {
@@ -413,14 +412,12 @@ static LLVMValueRef gen_field_ptr(CodeGen *g, AstNode *node, TypeTableEntry **ou
     assert(LLVMGetTypeKind(LLVMTypeOf(struct_ptr)) == LLVMPointerTypeKind);
     assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(struct_ptr))) == LLVMStructTypeKind);
 
-    FieldAccessNode *codegen_field_access = &node->codegen_node->data.field_access_node;
+    assert(node->data.field_access_expr.field_index >= 0);
 
-    assert(codegen_field_access->field_index >= 0);
-
-    *out_type_entry = codegen_field_access->type_struct_field->type_entry;
+    *out_type_entry = node->data.field_access_expr.type_struct_field->type_entry;
 
     add_debug_source_node(g, node);
-    return LLVMBuildStructGEP(g->builder, struct_ptr, codegen_field_access->field_index, "");
+    return LLVMBuildStructGEP(g->builder, struct_ptr, node->data.field_access_expr.field_index, "");
 }
 
 static LLVMValueRef gen_slice_expr(CodeGen *g, AstNode *node) {
@@ -429,7 +426,7 @@ static LLVMValueRef gen_slice_expr(CodeGen *g, AstNode *node) {
     AstNode *array_ref_node = node->data.slice_expr.array_ref_expr;
     TypeTableEntry *array_type = get_expr_type(array_ref_node);
 
-    LLVMValueRef tmp_struct_ptr = node->codegen_node->data.struct_val_expr_node.ptr;
+    LLVMValueRef tmp_struct_ptr = node->data.slice_expr.resolved_struct_val_expr.ptr;
     LLVMValueRef array_ptr = gen_array_base_ptr(g, array_ref_node);
 
     if (array_type->id == TypeTableEntryIdArray) {
@@ -545,8 +542,8 @@ static LLVMValueRef gen_lvalue(CodeGen *g, AstNode *expr_node, AstNode *node,
     LLVMValueRef target_ref;
 
     if (node->type == NodeTypeSymbol) {
-        VariableTableEntry *var = find_variable(expr_node->codegen_node->expr_node.block_context,
-                &node->data.symbol);
+        VariableTableEntry *var = find_variable(get_resolved_expr(expr_node)->block_context,
+                &node->data.symbol_expr.symbol);
         assert(var);
         // semantic checking ensures no variables are constant
         assert(!var->is_const);
@@ -630,7 +627,7 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {
 }
 
 static LLVMValueRef gen_bare_cast(CodeGen *g, AstNode *node, LLVMValueRef expr_val,
-        TypeTableEntry *actual_type, TypeTableEntry *wanted_type, CastNode *cast_node)
+        TypeTableEntry *actual_type, TypeTableEntry *wanted_type, Cast *cast_node)
 {
     switch (cast_node->op) {
         case CastOpNothing:
@@ -705,7 +702,7 @@ static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) {
     TypeTableEntry *actual_type = get_expr_type(node->data.cast_expr.expr);
     TypeTableEntry *wanted_type = get_expr_type(node);
 
-    CastNode *cast_node = &node->codegen_node->data.cast_node;
+    Cast *cast_node = &node->data.cast_expr.cast;
 
     return gen_bare_cast(g, node, expr_val, actual_type, wanted_type, cast_node);
 
@@ -1218,7 +1215,7 @@ static LLVMValueRef gen_if_var_expr(CodeGen *g, AstNode *node) {
     assert(node->data.if_var_expr.var_decl.expr);
 
     BlockContext *old_block_context = g->cur_block_context;
-    BlockContext *new_block_context = node->codegen_node->data.if_var_node.block_context;
+    BlockContext *new_block_context = node->data.if_var_expr.block_context;
 
     LLVMValueRef init_val;
     gen_var_decl_raw(g, node, &node->data.if_var_expr.var_decl, new_block_context, true, &init_val);
@@ -1242,7 +1239,7 @@ static LLVMValueRef gen_block(CodeGen *g, AstNode *block_node, TypeTableEntry *i
     assert(block_node->type == NodeTypeBlock);
 
     BlockContext *old_block_context = g->cur_block_context;
-    g->cur_block_context = block_node->codegen_node->data.block_node.block_context;
+    g->cur_block_context = block_node->data.block.block_context;
 
     LLVMValueRef return_value;
     for (int i = 0; i < block_node->data.block.statements.length; i += 1) {
@@ -1347,7 +1344,7 @@ static LLVMValueRef gen_asm_expr(CodeGen *g, AstNode *node) {
 
         if (!is_return) {
             VariableTableEntry *variable = find_variable(
-                    node->codegen_node->expr_node.block_context,
+                    get_resolved_expr(node)->block_context,
                     &asm_output->variable_name);
             assert(variable);
             param_types[param_index] = LLVMTypeOf(variable->value_ref);
@@ -1396,7 +1393,7 @@ static LLVMValueRef gen_null_literal(CodeGen *g, AstNode *node) {
     TypeTableEntry *type_entry = get_expr_type(node);
     assert(type_entry->id == TypeTableEntryIdMaybe);
 
-    LLVMValueRef tmp_struct_ptr = node->codegen_node->data.struct_val_expr_node.ptr;
+    LLVMValueRef tmp_struct_ptr = node->data.null_literal.resolved_struct_val_expr.ptr;
 
     add_debug_source_node(g, node);
     LLVMValueRef field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 1, "");
@@ -1416,12 +1413,13 @@ static LLVMValueRef gen_struct_val_expr(CodeGen *g, AstNode *node) {
     int field_count = type_entry->data.structure.field_count;
     assert(field_count == node->data.struct_val_expr.fields.length);
 
-    StructValExprNode *struct_val_expr_node = &node->codegen_node->data.struct_val_expr_node;
+    StructValExprCodeGen *struct_val_expr_node = &node->data.struct_val_expr.codegen;
     LLVMValueRef tmp_struct_ptr = struct_val_expr_node->ptr;
 
     for (int i = 0; i < field_count; i += 1) {
         AstNode *field_node = node->data.struct_val_expr.fields.at(i);
-        int index = field_node->codegen_node->data.struct_val_field_node.index;
+        assert(field_node->type == NodeTypeStructValueField);
+        int index = field_node->data.struct_val_field.index;
         TypeStructField *type_struct_field = &type_entry->data.structure.fields[index];
         assert(buf_eql_buf(type_struct_field->name, &field_node->data.struct_val_field.name));
 
@@ -1439,8 +1437,8 @@ static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) {
     assert(node->data.while_expr.condition);
     assert(node->data.while_expr.body);
 
-    bool condition_always_true = node->codegen_node->data.while_node.condition_always_true;
-    bool contains_break = node->codegen_node->data.while_node.contains_break;
+    bool condition_always_true = node->data.while_expr.condition_always_true;
+    bool contains_break = node->data.while_expr.contains_break;
     if (condition_always_true) {
         // generate a forever loop
 
@@ -1559,17 +1557,17 @@ static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVa
 static LLVMValueRef gen_var_decl_expr(CodeGen *g, AstNode *node) {
     LLVMValueRef init_val;
     return gen_var_decl_raw(g, node, &node->data.variable_declaration,
-            node->codegen_node->expr_node.block_context, false, &init_val);
+            get_resolved_expr(node)->block_context, false, &init_val);
 }
 
 static LLVMValueRef gen_number_literal_raw(CodeGen *g, AstNode *source_node,
-        NumberLiteralNode *codegen_num_lit, AstNodeNumberLiteral *num_lit_node)
+        NumLitCodeGen *codegen_num_lit, AstNodeNumberLiteral *num_lit_node)
 {
     TypeTableEntry *type_entry = codegen_num_lit->resolved_type;
     assert(type_entry);
 
     // override the expression type for number literals
-    source_node->codegen_node->expr_node.type_entry = type_entry;
+    get_resolved_expr(source_node)->type_entry = type_entry;
 
     if (type_entry->id == TypeTableEntryIdInt) {
         // here the union has int64_t and uint64_t and we purposefully read
@@ -1594,7 +1592,7 @@ static LLVMValueRef gen_compiler_fn_type(CodeGen *g, AstNode *node) {
     Buf *name = &node->data.compiler_fn_type.name;
     TypeTableEntry *type_entry = get_type_for_type_node(g, node->data.compiler_fn_type.type);
     if (buf_eql_str(name, "sizeof")) {
-        NumberLiteralNode *codegen_num_lit = &node->codegen_node->data.num_lit_node;
+        NumLitCodeGen *codegen_num_lit = get_resolved_num_lit(node);
         AstNodeNumberLiteral num_lit_node;
         num_lit_node.kind = type_entry->data.num_lit.kind;
         num_lit_node.overflow = false;
@@ -1632,7 +1630,7 @@ static LLVMValueRef gen_compiler_fn_type(CodeGen *g, AstNode *node) {
 static LLVMValueRef gen_number_literal(CodeGen *g, AstNode *node) {
     assert(node->type == NodeTypeNumberLiteral);
 
-    NumberLiteralNode *codegen_num_lit = &node->codegen_node->data.num_lit_node;
+    NumLitCodeGen *codegen_num_lit = get_resolved_num_lit(node);
     assert(codegen_num_lit);
 
     return gen_number_literal_raw(g, node, codegen_num_lit, &node->data.number_literal);
@@ -1664,7 +1662,7 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) {
         case NodeTypeVoid:
             return nullptr;
         case NodeTypeBoolLiteral:
-            if (node->data.bool_literal)
+            if (node->data.bool_literal.value)
                 return LLVMConstAllOnes(LLVMInt1Type());
             else
                 return LLVMConstNull(LLVMInt1Type());
@@ -1696,8 +1694,8 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) {
         case NodeTypeSymbol:
             {
                 VariableTableEntry *variable = find_variable(
-                        node->codegen_node->expr_node.block_context,
-                        &node->data.symbol);
+                        get_resolved_expr(node)->block_context,
+                        &node->data.symbol_expr.symbol);
                 assert(variable);
                 if (variable->type->id == TypeTableEntryIdVoid) {
                     return nullptr;
@@ -1721,14 +1719,14 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) {
             return gen_block(g, node, nullptr);
         case NodeTypeGoto:
             add_debug_source_node(g, node);
-            return LLVMBuildBr(g->builder, node->codegen_node->data.label_entry->basic_block);
+            return LLVMBuildBr(g->builder, node->data.goto_expr.label_entry->basic_block);
         case NodeTypeBreak:
             return gen_break(g, node);
         case NodeTypeContinue:
             return gen_continue(g, node);
         case NodeTypeLabel:
             {
-                LabelTableEntry *label_entry = node->codegen_node->data.label_entry;
+                LabelTableEntry *label_entry = node->data.label.label_entry;
                 assert(label_entry);
                 LLVMBasicBlockRef basic_block = label_entry->basic_block;
                 if (label_entry->entered_from_fallthrough) {
@@ -1770,19 +1768,19 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
         return val;
     }
 
-    assert(node->codegen_node);
+    Expr *expr = get_resolved_expr(node);
 
-    TypeTableEntry *before_type = node->codegen_node->expr_node.type_entry;
+    TypeTableEntry *before_type = expr->type_entry;
     if (before_type && before_type->id == TypeTableEntryIdUnreachable) {
         return val;
     }
-    CastNode *cast_node = &node->codegen_node->expr_node.implicit_cast;
+    Cast *cast_node = &expr->implicit_cast;
     if (cast_node->after_type) {
         val = gen_bare_cast(g, node, val, before_type, cast_node->after_type, cast_node);
         before_type = cast_node->after_type;
     }
 
-    cast_node = &node->codegen_node->expr_node.implicit_maybe_cast;
+    cast_node = &expr->implicit_maybe_cast;
     if (cast_node->after_type) {
         val = gen_bare_cast(g, node, val, before_type, cast_node->after_type, cast_node);
     }
@@ -1798,7 +1796,7 @@ static void build_label_blocks(CodeGen *g, AstNode *block_node) {
             continue;
 
         Buf *name = &label_node->data.label.name;
-        label_node->codegen_node->data.label_entry->basic_block = LLVMAppendBasicBlock(
+        label_node->data.label.label_entry->basic_block = LLVMAppendBasicBlock(
                 g->cur_fn->fn_value, buf_ptr(name));
     }
 
@@ -1944,13 +1942,7 @@ static void do_code_gen(CodeGen *g) {
         LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn, "entry");
         LLVMPositionBuilderAtEnd(g->builder, entry_block);
 
-        CodeGenNode *codegen_node = fn_def_node->codegen_node;
-        assert(codegen_node);
-
-        FnDefNode *codegen_fn_def = &codegen_node->data.fn_def_node;
-        assert(codegen_fn_def);
-
-        codegen_fn_def->block_context->di_scope = LLVMZigSubprogramToScope(subprogram);
+        fn_def_node->data.fn_def.block_context->di_scope = LLVMZigSubprogramToScope(subprogram);
 
         int non_void_param_count = count_non_void_params(g, &fn_proto->params);
         assert(non_void_param_count == (int)LLVMCountParams(fn));
@@ -1963,7 +1955,7 @@ static void do_code_gen(CodeGen *g) {
             assert(param_decl->type == NodeTypeParamDecl);
             if (is_param_decl_type_void(g, param_decl))
                 continue;
-            VariableTableEntry *parameter_variable = fn_def_node->codegen_node->data.fn_def_node.block_context->variable_table.get(&param_decl->data.param_decl.name);
+            VariableTableEntry *parameter_variable = fn_def_node->data.fn_def.block_context->variable_table.get(&param_decl->data.param_decl.name);
             parameter_variable->value_ref = params[non_void_index];
             non_void_index += 1;
         }
@@ -2019,14 +2011,14 @@ static void do_code_gen(CodeGen *g) {
 
             // allocate structs which are the result of casts
             for (int cea_i = 0; cea_i < block_context->cast_expr_alloca_list.length; cea_i += 1) {
-                CastNode *cast_node = block_context->cast_expr_alloca_list.at(cea_i);
+                Cast *cast_node = block_context->cast_expr_alloca_list.at(cea_i);
                 add_debug_source_node(g, cast_node->source_node);
                 cast_node->ptr = LLVMBuildAlloca(g->builder, cast_node->after_type->type_ref, "");
             }
 
             // allocate structs which are struct value expressions
             for (int alloca_i = 0; alloca_i < block_context->struct_val_expr_alloca_list.length; alloca_i += 1) {
-                StructValExprNode *struct_val_expr_node = block_context->struct_val_expr_alloca_list.at(alloca_i);
+                StructValExprCodeGen *struct_val_expr_node = block_context->struct_val_expr_alloca_list.at(alloca_i);
                 add_debug_source_node(g, struct_val_expr_node->source_node);
                 struct_val_expr_node->ptr = LLVMBuildAlloca(g->builder,
                         struct_val_expr_node->type_entry->type_ref, "");
@@ -2041,15 +2033,15 @@ static void do_code_gen(CodeGen *g) {
             if (is_param_decl_type_void(g, param_decl))
                 continue;
 
-            VariableTableEntry *variable = param_decl->codegen_node->data.param_decl_node.variable;
+            VariableTableEntry *variable = param_decl->data.param_decl.variable;
 
             LLVMZigDILocation *debug_loc = LLVMZigGetDebugLoc(param_decl->line + 1, param_decl->column + 1,
-                    codegen_fn_def->block_context->di_scope);
+                    fn_def_node->data.fn_def.block_context->di_scope);
             LLVMZigInsertDeclareAtEnd(g->dbuilder, variable->value_ref, variable->di_loc_var, debug_loc,
                     entry_block);
         }
 
-        TypeTableEntry *implicit_return_type = codegen_fn_def->implicit_return_type;
+        TypeTableEntry *implicit_return_type = fn_def_node->data.fn_def.implicit_return_type;
         gen_block(g, fn_def_node->data.fn_def.body, implicit_return_type);
 
     }
@@ -2549,8 +2541,6 @@ static ImportTableEntry *codegen_add_code(CodeGen *g, Buf *abs_full_path,
             Buf *import_code = buf_alloc();
             bool found_it = false;
 
-            alloc_codegen_node(top_level_decl);
-
             for (int path_i = 0; path_i < g->lib_search_paths.length; path_i += 1) {
                 Buf *search_path = g->lib_search_paths.at(path_i);
                 os_path_join(search_path, import_target_path, &full_path);
@@ -2570,7 +2560,7 @@ static ImportTableEntry *codegen_add_code(CodeGen *g, Buf *abs_full_path,
                 auto entry = g->import_table.maybe_get(abs_full_path);
                 if (entry) {
                     found_it = true;
-                    top_level_decl->codegen_node->data.import_node.import = entry->value;
+                    top_level_decl->data.use.import = entry->value;
                 } else {
                     if ((err = os_fetch_file_path(abs_full_path, import_code))) {
                         if (err == ErrorFileNotFound) {
@@ -2582,7 +2572,7 @@ static ImportTableEntry *codegen_add_code(CodeGen *g, Buf *abs_full_path,
                             goto done_looking_at_imports;
                         }
                     }
-                    top_level_decl->codegen_node->data.import_node.import = codegen_add_code(g,
+                    top_level_decl->data.use.import = codegen_add_code(g,
                             abs_full_path, search_path, &top_level_decl->data.use.path, import_code);
                     found_it = true;
                 }
@@ -2682,9 +2672,8 @@ void codegen_add_root_code(CodeGen *g, Buf *src_dir, Buf *src_basename, Buf *sou
 
 static void to_c_type(CodeGen *g, AstNode *type_node, Buf *out_buf) {
     assert(type_node->type == NodeTypeType);
-    assert(type_node->codegen_node);
 
-    TypeTableEntry *type_entry = type_node->codegen_node->data.type_node.entry;
+    TypeTableEntry *type_entry = type_node->data.type.entry;
     assert(type_entry);
 
     if (type_entry == g->builtin_types.entry_u8) {
src/codegen.hpp
@@ -11,21 +11,8 @@
 #include "parser.hpp"
 #include "errmsg.hpp"
 
-struct CodeGen;
-
-enum OutType {
-    OutTypeUnknown,
-    OutTypeExe,
-    OutTypeLib,
-    OutTypeObj,
-};
-
 CodeGen *codegen_create(Buf *root_source_dir);
 
-enum CodeGenBuildType {
-    CodeGenBuildTypeDebug,
-    CodeGenBuildTypeRelease,
-};
 void codegen_set_build_type(CodeGen *codegen, CodeGenBuildType build_type);
 void codegen_set_is_static(CodeGen *codegen, bool is_static);
 void codegen_set_strip(CodeGen *codegen, bool strip);
src/parser.cpp
@@ -356,8 +356,7 @@ void ast_print(AstNode *node, int indent) {
             fprintf(stderr, "Unreachable\n");
             break;
         case NodeTypeSymbol:
-            fprintf(stderr, "Symbol %s\n",
-                    buf_ptr(&node->data.symbol));
+            fprintf(stderr, "Symbol %s\n", buf_ptr(&node->data.symbol_expr.symbol));
             break;
         case NodeTypeUse:
             fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.use.path));
@@ -366,7 +365,8 @@ void ast_print(AstNode *node, int indent) {
             fprintf(stderr, "%s\n", node_type_str(node->type));
             break;
         case NodeTypeBoolLiteral:
-            fprintf(stderr, "%s '%s'\n", node_type_str(node->type), node->data.bool_literal ? "true" : "false");
+            fprintf(stderr, "%s '%s'\n", node_type_str(node->type),
+                    node->data.bool_literal.value ? "true" : "false");
             break;
         case NodeTypeNullLiteral:
             fprintf(stderr, "%s\n", node_type_str(node->type));
@@ -401,7 +401,7 @@ void ast_print(AstNode *node, int indent) {
             fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.label.name));
             break;
         case NodeTypeGoto:
-            fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.go_to.name));
+            fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.goto_expr.name));
             break;
         case NodeTypeBreak:
             fprintf(stderr, "%s\n", node_type_str(node->type));
@@ -1369,12 +1369,12 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool
         return node;
     } else if (token->id == TokenIdKeywordTrue) {
         AstNode *node = ast_create_node(pc, NodeTypeBoolLiteral, token);
-        node->data.bool_literal = true;
+        node->data.bool_literal.value = true;
         *token_index += 1;
         return node;
     } else if (token->id == TokenIdKeywordFalse) {
         AstNode *node = ast_create_node(pc, NodeTypeBoolLiteral, token);
-        node->data.bool_literal = false;
+        node->data.bool_literal.value = false;
         *token_index += 1;
         return node;
     } else if (token->id == TokenIdKeywordNull) {
@@ -1385,7 +1385,7 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool
         *token_index += 1;
         Token *name_tok = ast_eat_token(pc, token_index, TokenIdSymbol);
         AstNode *name_node = ast_create_node(pc, NodeTypeSymbol, name_tok);
-        ast_buf_from_token(pc, name_tok, &name_node->data.symbol);
+        ast_buf_from_token(pc, name_tok, &name_node->data.symbol_expr.symbol);
 
         AstNode *node = ast_create_node(pc, NodeTypeFnCallExpr, token);
         node->data.fn_call_expr.fn_ref_expr = name_node;
@@ -1401,7 +1401,7 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool
         } else {
             *token_index += 1;
             AstNode *node = ast_create_node(pc, NodeTypeSymbol, token);
-            ast_buf_from_token(pc, token, &node->data.symbol);
+            ast_buf_from_token(pc, token, &node->data.symbol_expr.symbol);
             return node;
         }
     } else if (token->id == TokenIdKeywordGoto) {
@@ -1412,7 +1412,7 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool
         *token_index += 1;
         ast_expect_token(pc, dest_symbol, TokenIdSymbol);
 
-        ast_buf_from_token(pc, dest_symbol, &node->data.go_to.name);
+        ast_buf_from_token(pc, dest_symbol, &node->data.goto_expr.name);
         return node;
     } else if (token->id == TokenIdKeywordBreak) {
         AstNode *node = ast_create_node(pc, NodeTypeBreak, token);
src/parser.hpp
@@ -8,450 +8,10 @@
 #ifndef ZIG_PARSER_HPP
 #define ZIG_PARSER_HPP
 
-#include "list.hpp"
-#include "buffer.hpp"
+#include "all_types.hpp"
 #include "tokenizer.hpp"
 #include "errmsg.hpp"
 
-struct AstNode;
-struct CodeGenNode;
-struct ImportTableEntry;
-struct AsmToken;
-
-enum NodeType {
-    NodeTypeRoot,
-    NodeTypeRootExportDecl,
-    NodeTypeFnProto,
-    NodeTypeFnDef,
-    NodeTypeFnDecl,
-    NodeTypeParamDecl,
-    NodeTypeType,
-    NodeTypeBlock,
-    NodeTypeExternBlock,
-    NodeTypeDirective,
-    NodeTypeReturnExpr,
-    NodeTypeVariableDeclaration,
-    NodeTypeBinOpExpr,
-    NodeTypeCastExpr,
-    NodeTypeNumberLiteral,
-    NodeTypeStringLiteral,
-    NodeTypeCharLiteral,
-    NodeTypeUnreachable,
-    NodeTypeSymbol,
-    NodeTypePrefixOpExpr,
-    NodeTypeFnCallExpr,
-    NodeTypeArrayAccessExpr,
-    NodeTypeSliceExpr,
-    NodeTypeFieldAccessExpr,
-    NodeTypeUse,
-    NodeTypeVoid,
-    NodeTypeBoolLiteral,
-    NodeTypeNullLiteral,
-    NodeTypeIfBoolExpr,
-    NodeTypeIfVarExpr,
-    NodeTypeWhileExpr,
-    NodeTypeLabel,
-    NodeTypeGoto,
-    NodeTypeBreak,
-    NodeTypeContinue,
-    NodeTypeAsmExpr,
-    NodeTypeStructDecl,
-    NodeTypeStructField,
-    NodeTypeStructValueExpr,
-    NodeTypeStructValueField,
-    NodeTypeEnumDecl,
-    NodeTypeEnumField,
-    NodeTypeCompilerFnExpr,
-    NodeTypeCompilerFnType,
-};
-
-struct AstNodeRoot {
-    ZigList<AstNode *> top_level_decls;
-};
-
-enum VisibMod {
-    VisibModPrivate,
-    VisibModPub,
-    VisibModExport,
-};
-
-struct AstNodeFnProto {
-    ZigList<AstNode *> *directives;
-    VisibMod visib_mod;
-    Buf name;
-    ZigList<AstNode *> params;
-    AstNode *return_type;
-    bool is_var_args;
-
-    // the extern block this fn proto is inside. can be null.
-    // populated by semantic analyzer.
-    AstNode *extern_node;
-    // the struct decl node this fn proto is inside. can be null.
-    // populated by semantic analyzer.
-    AstNode *struct_node;
-    // the function definition this fn proto is inside. can be null.
-    // populated by semantic analyzer.
-    AstNode *fn_def_node;
-};
-
-struct AstNodeFnDef {
-    AstNode *fn_proto;
-    AstNode *body;
-};
-
-struct AstNodeFnDecl {
-    AstNode *fn_proto;
-};
-
-struct AstNodeParamDecl {
-    Buf name;
-    AstNode *type;
-};
-
-enum AstNodeTypeType {
-    AstNodeTypeTypePrimitive,
-    AstNodeTypeTypePointer,
-    AstNodeTypeTypeArray,
-    AstNodeTypeTypeMaybe,
-    AstNodeTypeTypeCompilerExpr,
-};
-
-struct AstNodeType {
-    AstNodeTypeType type;
-    Buf primitive_name;
-    AstNode *child_type;
-    AstNode *array_size; // can be null
-    bool is_const;
-    bool is_noalias;
-    AstNode *compiler_expr;
-};
-
-struct AstNodeBlock {
-    ZigList<AstNode *> statements;
-};
-
-struct AstNodeReturnExpr {
-    // might be null in case of return void;
-    AstNode *expr;
-};
-
-struct AstNodeVariableDeclaration {
-    Buf symbol;
-    bool is_const;
-    VisibMod visib_mod;
-    // one or both of type and expr will be non null
-    AstNode *type;
-    AstNode *expr;
-};
-
-enum BinOpType {
-    BinOpTypeInvalid,
-    BinOpTypeAssign,
-    BinOpTypeAssignTimes,
-    BinOpTypeAssignDiv,
-    BinOpTypeAssignMod,
-    BinOpTypeAssignPlus,
-    BinOpTypeAssignMinus,
-    BinOpTypeAssignBitShiftLeft,
-    BinOpTypeAssignBitShiftRight,
-    BinOpTypeAssignBitAnd,
-    BinOpTypeAssignBitXor,
-    BinOpTypeAssignBitOr,
-    BinOpTypeAssignBoolAnd,
-    BinOpTypeAssignBoolOr,
-    BinOpTypeBoolOr,
-    BinOpTypeBoolAnd,
-    BinOpTypeCmpEq,
-    BinOpTypeCmpNotEq,
-    BinOpTypeCmpLessThan,
-    BinOpTypeCmpGreaterThan,
-    BinOpTypeCmpLessOrEq,
-    BinOpTypeCmpGreaterOrEq,
-    BinOpTypeBinOr,
-    BinOpTypeBinXor,
-    BinOpTypeBinAnd,
-    BinOpTypeBitShiftLeft,
-    BinOpTypeBitShiftRight,
-    BinOpTypeAdd,
-    BinOpTypeSub,
-    BinOpTypeMult,
-    BinOpTypeDiv,
-    BinOpTypeMod,
-    BinOpTypeUnwrapMaybe,
-};
-
-struct AstNodeBinOpExpr {
-    AstNode *op1;
-    BinOpType bin_op;
-    AstNode *op2;
-};
-
-struct AstNodeFnCallExpr {
-    AstNode *fn_ref_expr;
-    ZigList<AstNode *> params;
-    bool is_builtin;
-};
-
-struct AstNodeArrayAccessExpr {
-    AstNode *array_ref_expr;
-    AstNode *subscript;
-};
-
-struct AstNodeSliceExpr {
-    AstNode *array_ref_expr;
-    AstNode *start;
-    AstNode *end;
-    bool is_const;
-};
-
-struct AstNodeFieldAccessExpr {
-    AstNode *struct_expr;
-    Buf field_name;
-};
-
-struct AstNodeExternBlock {
-    ZigList<AstNode *> *directives;
-    ZigList<AstNode *> fn_decls;
-};
-
-struct AstNodeDirective {
-    Buf name;
-    Buf param;
-};
-
-struct AstNodeRootExportDecl {
-    Buf type;
-    Buf name;
-    ZigList<AstNode *> *directives;
-};
-
-struct AstNodeCastExpr {
-    AstNode *expr;
-    AstNode *type;
-};
-
-enum PrefixOp {
-    PrefixOpInvalid,
-    PrefixOpBoolNot,
-    PrefixOpBinNot,
-    PrefixOpNegation,
-    PrefixOpAddressOf,
-    PrefixOpConstAddressOf,
-    PrefixOpDereference,
-};
-
-struct AstNodePrefixOpExpr {
-    PrefixOp prefix_op;
-    AstNode *primary_expr;
-};
-
-struct AstNodeUse {
-    Buf path;
-    ZigList<AstNode *> *directives;
-};
-
-struct AstNodeIfBoolExpr {
-    AstNode *condition;
-    AstNode *then_block;
-    AstNode *else_node; // null, block node, or other if expr node
-};
-
-struct AstNodeIfVarExpr {
-    AstNodeVariableDeclaration var_decl;
-    AstNode *then_block;
-    AstNode *else_node; // null, block node, or other if expr node
-};
-
-struct AstNodeWhileExpr {
-    AstNode *condition;
-    AstNode *body;
-};
-
-struct AstNodeLabel {
-    Buf name;
-};
-
-struct AstNodeGoto {
-    Buf name;
-};
-
-struct AsmOutput {
-    Buf asm_symbolic_name;
-    Buf constraint;
-    Buf variable_name;
-    AstNode *return_type; // null unless "=r" and return
-};
-
-struct AsmInput {
-    Buf asm_symbolic_name;
-    Buf constraint;
-    AstNode *expr;
-};
-
-struct SrcPos {
-    int line;
-    int column;
-};
-
-struct AstNodeAsmExpr {
-    bool is_volatile;
-    Buf asm_template;
-    ZigList<SrcPos> offset_map;
-    ZigList<AsmToken> token_list;
-    ZigList<AsmOutput*> output_list;
-    ZigList<AsmInput*> input_list;
-    ZigList<Buf*> clobber_list;
-    int return_count; // populated by analyze
-};
-
-struct AstNodeStructDecl {
-    Buf name;
-    ZigList<AstNode *> fields;
-    ZigList<AstNode *> fns;
-    ZigList<AstNode *> *directives;
-    VisibMod visib_mod;
-};
-
-struct AstNodeStructField {
-    Buf name;
-    AstNode *type;
-    ZigList<AstNode *> *directives;
-};
-
-struct AstNodeEnumDecl {
-    Buf name;
-    ZigList<AstNode *> fields;
-    ZigList<AstNode *> *directives;
-    VisibMod visib_mod;
-};
-
-struct AstNodeEnumField {
-    Buf name;
-    ZigList<AstNode *> fields; // length 0 means simple enum
-    AstNode *val_expr;
-};
-
-struct AstNodeStringLiteral {
-    Buf buf;
-    bool c;
-};
-
-struct AstNodeCharLiteral {
-    uint8_t value;
-};
-
-enum NumLit {
-    NumLitF32,
-    NumLitF64,
-    NumLitF128,
-    NumLitU8,
-    NumLitU16,
-    NumLitU32,
-    NumLitU64,
-    NumLitI8,
-    NumLitI16,
-    NumLitI32,
-    NumLitI64,
-
-    NumLitCount
-};
-
-struct AstNodeNumberLiteral {
-    NumLit kind;
-
-    // overflow is true if when parsing the number, we discovered it would not
-    // fit without losing data in a uint64_t, int64_t, or double
-    bool overflow;
-
-    union {
-        uint64_t x_uint;
-        int64_t x_int;
-        double x_float;
-    } data;
-};
-
-struct AstNodeStructValueField {
-    Buf name;
-    AstNode *expr;
-};
-
-struct AstNodeStructValueExpr {
-    AstNode *type;
-    ZigList<AstNode *> fields;
-};
-
-struct AstNodeCompilerFnExpr {
-    Buf name;
-    AstNode *expr;
-};
-
-struct AstNodeCompilerFnType {
-    Buf name;
-    AstNode *type;
-};
-
-struct AstNode {
-    enum NodeType type;
-    int line;
-    int column;
-    uint32_t create_index; // for determinism purposes
-    CodeGenNode *codegen_node;
-    ImportTableEntry *owner;
-    union {
-        AstNodeRoot root;
-        AstNodeRootExportDecl root_export_decl;
-        AstNodeFnDef fn_def;
-        AstNodeFnDecl fn_decl;
-        AstNodeFnProto fn_proto;
-        AstNodeType type;
-        AstNodeParamDecl param_decl;
-        AstNodeBlock block;
-        AstNodeReturnExpr return_expr;
-        AstNodeVariableDeclaration variable_declaration;
-        AstNodeBinOpExpr bin_op_expr;
-        AstNodeExternBlock extern_block;
-        AstNodeDirective directive;
-        AstNodeCastExpr cast_expr;
-        AstNodePrefixOpExpr prefix_op_expr;
-        AstNodeFnCallExpr fn_call_expr;
-        AstNodeArrayAccessExpr array_access_expr;
-        AstNodeSliceExpr slice_expr;
-        AstNodeUse use;
-        AstNodeIfBoolExpr if_bool_expr;
-        AstNodeIfVarExpr if_var_expr;
-        AstNodeWhileExpr while_expr;
-        AstNodeLabel label;
-        AstNodeGoto go_to;
-        AstNodeAsmExpr asm_expr;
-        AstNodeFieldAccessExpr field_access_expr;
-        AstNodeStructDecl struct_decl;
-        AstNodeStructField struct_field;
-        AstNodeEnumDecl enum_decl;
-        AstNodeEnumField enum_field;
-        AstNodeStringLiteral string_literal;
-        AstNodeCharLiteral char_literal;
-        AstNodeNumberLiteral number_literal;
-        AstNodeStructValueExpr struct_val_expr;
-        AstNodeStructValueField struct_val_field;
-        AstNodeCompilerFnExpr compiler_fn_expr;
-        AstNodeCompilerFnType compiler_fn_type;
-        Buf symbol;
-        bool bool_literal;
-    } data;
-};
-
-enum AsmTokenId {
-    AsmTokenIdTemplate,
-    AsmTokenIdPercent,
-    AsmTokenIdVar,
-};
-
-struct AsmToken {
-    enum AsmTokenId id;
-    int start;
-    int end;
-};
-
 __attribute__ ((format (printf, 2, 3)))
 void ast_token_error(Token *token, const char *format, ...);