Commit 84f7805029

Andrew Kelley <superjoe30@gmail.com>
2016-11-27 02:52:22
IR: support import builtin function
1 parent 4619b5d
src/all_types.hpp
@@ -117,12 +117,6 @@ enum ReturnKnowledge {
     ReturnKnowledgeSkipDefers,
 };
 
-struct Expr {
-    IrInstruction *instruction;
-    ReturnKnowledge return_knowledge;
-    VariableTableEntry *variable;
-};
-
 struct StructValExprCodeGen {
     TypeTableEntry *type_entry;
     LLVMValueRef ptr;
@@ -152,6 +146,7 @@ struct TopLevelDecl {
     bool dep_loop_flag;
     TldResolution resolution;
     AstNode *parent_decl;
+    IrInstruction *value;
 };
 
 struct TypeEnumField {
@@ -232,7 +227,6 @@ struct AstNodeFnProto {
     AstNode *fn_def_node;
     FnTableEntry *fn_table_entry;
     bool skip;
-    Expr resolved_expr;
     // computed from params field
     size_t inline_arg_count;
     size_t inline_or_var_type_arg_count;
@@ -274,7 +268,6 @@ struct AstNodeBlock {
     // you can follow its parents up to child_block. it will equal
     // child_block if there are no defers or var decls in the block.
     BlockContext *nested_block;
-    Expr resolved_expr;
 };
 
 enum ReturnKind {
@@ -287,9 +280,6 @@ struct AstNodeReturnExpr {
     ReturnKind kind;
     // might be null in case of return void;
     AstNode *expr;
-
-    // populated by semantic analyzer:
-    Expr resolved_expr;
 };
 
 struct AstNodeDefer {
@@ -297,7 +287,6 @@ struct AstNodeDefer {
     AstNode *expr;
 
     // populated by semantic analyzer:
-    Expr resolved_expr;
     size_t index_in_block;
     LLVMBasicBlockRef basic_block;
     BlockContext *child_block;
@@ -314,7 +303,6 @@ struct AstNodeVariableDeclaration {
     AstNode *expr;
 
     // populated by semantic analyzer
-    Expr resolved_expr;
     VariableTableEntry *variable;
 };
 
@@ -392,7 +380,6 @@ struct AstNodeBinOpExpr {
     // populated by semantic analyzer:
     // for when op is BinOpTypeAssign
     VariableTableEntry *var_entry;
-    Expr resolved_expr;
 };
 
 struct AstNodeUnwrapErrorExpr {
@@ -401,7 +388,6 @@ struct AstNodeUnwrapErrorExpr {
     AstNode *op2;
 
     // populated by semantic analyzer:
-    Expr resolved_expr;
     VariableTableEntry *var;
 };
 
@@ -434,7 +420,6 @@ struct AstNodeFnCallExpr {
 
     // populated by semantic analyzer:
     BuiltinFnEntry *builtin_fn;
-    Expr resolved_expr;
     FnTableEntry *fn_entry;
     CastOp cast_op;
     // if cast_op is CastOpArrayToString, this will be a pointer to
@@ -447,7 +432,6 @@ struct AstNodeArrayAccessExpr {
     AstNode *subscript;
 
     // populated by semantic analyzer:
-    Expr resolved_expr;
 };
 
 struct AstNodeSliceExpr {
@@ -457,7 +441,6 @@ struct AstNodeSliceExpr {
     bool is_const;
 
     // populated by semantic analyzer:
-    Expr resolved_expr;
     StructValExprCodeGen resolved_struct_val_expr;
 };
 
@@ -468,7 +451,6 @@ struct AstNodeFieldAccessExpr {
     // populated by semantic analyzer
     TypeStructField *type_struct_field;
     TypeEnumField *type_enum_field;
-    Expr resolved_expr;
     StructValExprCodeGen resolved_struct_val_expr; // for enum values
     TypeTableEntry *bare_container_type;
     bool is_member_fn;
@@ -495,7 +477,6 @@ struct AstNodePrefixOpExpr {
     AstNode *primary_expr;
 
     // populated by semantic analyzer
-    Expr resolved_expr;
 };
 
 struct AstNodeUse {
@@ -511,7 +492,6 @@ struct AstNodeIfBoolExpr {
     AstNode *else_node; // null, block node, or other if expr node
 
     // populated by semantic analyzer
-    Expr resolved_expr;
 };
 
 struct AstNodeIfVarExpr {
@@ -522,7 +502,6 @@ struct AstNodeIfVarExpr {
 
     // populated by semantic analyzer
     TypeTableEntry *type;
-    Expr resolved_expr;
 };
 
 struct AstNodeWhileExpr {
@@ -535,7 +514,6 @@ struct AstNodeWhileExpr {
     bool condition_always_true;
     bool contains_break;
     bool contains_continue;
-    Expr resolved_expr;
 };
 
 struct AstNodeForExpr {
@@ -549,7 +527,6 @@ struct AstNodeForExpr {
     // populated by semantic analyzer
     bool contains_break;
     bool contains_continue;
-    Expr resolved_expr;
     VariableTableEntry *elem_var;
     VariableTableEntry *index_var;
 };
@@ -560,7 +537,6 @@ struct AstNodeSwitchExpr {
     bool is_inline;
 
     // populated by semantic analyzer
-    Expr resolved_expr;
 };
 
 struct AstNodeSwitchProng {
@@ -580,7 +556,6 @@ struct AstNodeLabel {
     Buf *name;
 
     // populated by semantic analyzer
-    Expr resolved_expr;
     LabelTableEntry *label_entry;
 };
 
@@ -588,7 +563,6 @@ struct AstNodeGoto {
     Buf *name;
 
     // populated by semantic analyzer
-    Expr resolved_expr;
     LabelTableEntry *label_entry;
 };
 
@@ -634,7 +608,6 @@ struct AstNodeAsmExpr {
     ZigList<Buf*> clobber_list;
 
     // populated by semantic analyzer
-    Expr resolved_expr;
 };
 
 enum ContainerKind {
@@ -670,14 +643,12 @@ struct AstNodeStringLiteral {
     bool c;
 
     // populated by semantic analyzer:
-    Expr resolved_expr;
 };
 
 struct AstNodeCharLiteral {
     uint8_t value;
 
     // populated by semantic analyzer:
-    Expr resolved_expr;
 };
 
 struct AstNodeNumberLiteral {
@@ -688,7 +659,6 @@ struct AstNodeNumberLiteral {
     bool overflow;
 
     // populated by semantic analyzer
-    Expr resolved_expr;
 };
 
 struct AstNodeStructValueField {
@@ -711,35 +681,29 @@ struct AstNodeContainerInitExpr {
 
     // populated by semantic analyzer
     StructValExprCodeGen resolved_struct_val_expr;
-    Expr resolved_expr;
     TypeTableEntry *enum_type;
 };
 
 struct AstNodeNullLiteral {
     // populated by semantic analyzer
-    Expr resolved_expr;
 };
 
 struct AstNodeUndefinedLiteral {
     // populated by semantic analyzer
-    Expr resolved_expr;
 };
 
 struct AstNodeZeroesLiteral {
     // populated by semantic analyzer
-    Expr resolved_expr;
 };
 
 struct AstNodeThisLiteral {
     // populated by semantic analyzer
-    Expr resolved_expr;
 };
 
 struct AstNodeSymbolExpr {
     Buf *symbol;
 
     // populated by semantic analyzer
-    Expr resolved_expr;
     TypeEnumField *enum_field;
     uint32_t err_value;
 };
@@ -748,17 +712,14 @@ 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 AstNodeArrayType {
@@ -767,22 +728,18 @@ struct AstNodeArrayType {
     bool is_const;
 
     // populated by semantic analyzer
-    Expr resolved_expr;
 };
 
 struct AstNodeErrorType {
     // populated by semantic analyzer
-    Expr resolved_expr;
 };
 
 struct AstNodeTypeLiteral {
     // populated by semantic analyzer
-    Expr resolved_expr;
 };
 
 struct AstNodeVarLiteral {
     // populated by semantic analyzer
-    Expr resolved_expr;
 };
 
 struct AstNode {
@@ -1329,7 +1286,6 @@ struct CodeGen {
     LLVMValueRef err_name_table;
 
     IrInstruction *invalid_instruction;
-    Buf *len_buf;
 };
 
 struct VariableTableEntry {
@@ -1389,9 +1345,6 @@ struct BlockContext {
     ZigLLVMDIScope *di_scope;
     Buf *c_import_buf;
 
-    // if this is true, then this code will not be generated
-    bool codegen_excluded;
-
     bool safety_off;
     AstNode *safety_set_node;
 };
@@ -1433,7 +1386,6 @@ enum IrInstructionId {
     IrInstructionIdStorePtr,
     IrInstructionIdFieldPtr,
     IrInstructionIdStructFieldPtr,
-    IrInstructionIdReadField,
     IrInstructionIdElemPtr,
     IrInstructionIdVarPtr,
     IrInstructionIdCall,
@@ -1460,6 +1412,8 @@ enum IrInstructionId {
     IrInstructionIdClz,
     IrInstructionIdCtz,
     IrInstructionIdStaticEval,
+    IrInstructionIdImport,
+    IrInstructionIdArrayLen,
 };
 
 struct IrInstruction {
@@ -1628,13 +1582,6 @@ struct IrInstructionStructFieldPtr {
     bool is_const;
 };
 
-struct IrInstructionReadField {
-    IrInstruction base;
-
-    IrInstruction *container_ptr;
-    Buf *field_name;
-};
-
 struct IrInstructionElemPtr {
     IrInstruction base;
 
@@ -1816,6 +1763,18 @@ struct IrInstructionStaticEval {
     IrInstruction *value;
 };
 
+struct IrInstructionImport {
+    IrInstruction base;
+
+    IrInstruction *name;
+};
+
+struct IrInstructionArrayLen {
+    IrInstruction base;
+
+    IrInstruction *array_value;
+};
+
 enum LValPurpose {
     LValPurposeNone,
     LValPurposeAssign,
src/analyze.cpp
@@ -19,7 +19,6 @@
 
 static void resolve_enum_type(CodeGen *g, ImportTableEntry *import, TypeTableEntry *enum_type);
 static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableEntry *struct_type);
-static void scan_decls(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node);
 
 AstNode *first_executing_node(AstNode *node) {
     switch (node->type) {
@@ -1670,7 +1669,7 @@ static void preview_error_value_decl(CodeGen *g, AstNode *node) {
     node->data.error_value_decl.top_level_decl.resolution = TldResolutionOk;
 }
 
-static void scan_decls(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) {
+void scan_decls(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) {
     switch (node->type) {
         case NodeTypeRoot:
             for (size_t i = 0; i < import->root->data.root.top_level_decls.length; i += 1) {
@@ -1947,9 +1946,8 @@ static void resolve_var_decl(CodeGen *g, ImportTableEntry *import, AstNode *node
             implicit_type = g->builtin_types.entry_invalid;
         }
         if (implicit_type->id != TypeTableEntryIdInvalid) {
-            Expr *expr = get_resolved_expr(var_decl->expr);
             assert(result->static_value.special != ConstValSpecialRuntime);
-            expr->instruction = result;
+            var_decl->top_level_decl.value = result;
         }
     } else if (!is_extern) {
         add_node_error(g, node, buf_sprintf("variables must be initialized"));
@@ -2206,7 +2204,6 @@ BlockContext *new_block_context(AstNode *node, BlockContext *parent) {
     if (parent) {
         context->parent_loop_node = parent->parent_loop_node;
         context->c_import_buf = parent->c_import_buf;
-        context->codegen_excluded = parent->codegen_excluded;
     }
 
     if (node && node->type == NodeTypeFnDef) {
@@ -2455,17 +2452,16 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {
 
 static void add_symbols_from_import(CodeGen *g, AstNode *src_use_node, AstNode *dst_use_node) {
     TopLevelDecl *tld = get_as_top_level_decl(dst_use_node);
-    AstNode *use_target_node = src_use_node->data.use.expr;
-    Expr *expr = get_resolved_expr(use_target_node);
 
-    if (expr->instruction->type_entry->id == TypeTableEntryIdInvalid) {
+    IrInstruction *use_target_value = tld->value;
+    if (use_target_value->type_entry->id == TypeTableEntryIdInvalid) {
         tld->import->any_imports_failed = true;
         return;
     }
 
     tld->resolution = TldResolutionOk;
 
-    ConstExprValue *const_val = &expr->instruction->static_value;
+    ConstExprValue *const_val = &use_target_value->static_value;
     assert(const_val->special != ConstValSpecialRuntime);
 
     ImportTableEntry *target_import = const_val->data.x_import;
@@ -2511,7 +2507,7 @@ static void add_symbols_from_import(CodeGen *g, AstNode *src_use_node, AstNode *
 
 }
 
-static void resolve_use_decl(CodeGen *g, AstNode *node) {
+void resolve_use_decl(CodeGen *g, AstNode *node) {
     assert(node->type == NodeTypeUse);
     if (get_as_top_level_decl(node)->resolution != TldResolutionUnresolved) {
         return;
@@ -2519,7 +2515,7 @@ static void resolve_use_decl(CodeGen *g, AstNode *node) {
     add_symbols_from_import(g, node, node);
 }
 
-static void preview_use_decl(CodeGen *g, AstNode *node) {
+void preview_use_decl(CodeGen *g, AstNode *node) {
     assert(node->type == NodeTypeUse);
     TopLevelDecl *tld = get_as_top_level_decl(node);
 
@@ -2637,97 +2633,6 @@ void semantic_analyze(CodeGen *g) {
     }
 }
 
-Expr *get_resolved_expr(AstNode *node) {
-    switch (node->type) {
-        case NodeTypeReturnExpr:
-            return &node->data.return_expr.resolved_expr;
-        case NodeTypeDefer:
-            return &node->data.defer.resolved_expr;
-        case NodeTypeBinOpExpr:
-            return &node->data.bin_op_expr.resolved_expr;
-        case NodeTypeUnwrapErrorExpr:
-            return &node->data.unwrap_err_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 NodeTypeForExpr:
-            return &node->data.for_expr.resolved_expr;
-        case NodeTypeAsmExpr:
-            return &node->data.asm_expr.resolved_expr;
-        case NodeTypeContainerInitExpr:
-            return &node->data.container_init_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 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 NodeTypeUndefinedLiteral:
-            return &node->data.undefined_literal.resolved_expr;
-        case NodeTypeZeroesLiteral:
-            return &node->data.zeroes_literal.resolved_expr;
-        case NodeTypeThisLiteral:
-            return &node->data.this_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 NodeTypeLabel:
-            return &node->data.label.resolved_expr;
-        case NodeTypeArrayType:
-            return &node->data.array_type.resolved_expr;
-        case NodeTypeErrorType:
-            return &node->data.error_type.resolved_expr;
-        case NodeTypeTypeLiteral:
-            return &node->data.type_literal.resolved_expr;
-        case NodeTypeSwitchExpr:
-            return &node->data.switch_expr.resolved_expr;
-        case NodeTypeFnProto:
-            return &node->data.fn_proto.resolved_expr;
-        case NodeTypeVarLiteral:
-            return &node->data.var_literal.resolved_expr;
-        case NodeTypeSwitchProng:
-        case NodeTypeSwitchRange:
-        case NodeTypeRoot:
-        case NodeTypeFnDef:
-        case NodeTypeFnDecl:
-        case NodeTypeParamDecl:
-        case NodeTypeUse:
-        case NodeTypeContainerDecl:
-        case NodeTypeStructField:
-        case NodeTypeStructValueField:
-        case NodeTypeErrorValueDecl:
-        case NodeTypeTypeDecl:
-            zig_unreachable();
-    }
-    zig_unreachable();
-}
-
 TopLevelDecl *get_as_top_level_decl(AstNode *node) {
     switch (node->type) {
         case NodeTypeVariableDeclaration:
@@ -2997,40 +2902,42 @@ static uint32_t hash_const_val(TypeTableEntry *type, ConstExprValue *const_val)
 }
 
 uint32_t generic_fn_type_id_hash(GenericFnTypeId *id) {
-    uint32_t result = 0;
-    result += hash_ptr(id->decl_node);
-    for (size_t i = 0; i < id->generic_param_count; i += 1) {
-        GenericParamValue *generic_param = &id->generic_params[i];
-        if (generic_param->node) {
-            ConstExprValue *const_val = &get_resolved_expr(generic_param->node)->instruction->static_value;
-            assert(const_val->special != ConstValSpecialRuntime);
-            result += hash_const_val(generic_param->type, const_val);
-        }
-        result += hash_ptr(generic_param->type);
-    }
-    return result;
+    zig_panic("TODO generic_fn_type_id_hash");
+    //uint32_t result = 0;
+    //result += hash_ptr(id->decl_node);
+    //for (size_t i = 0; i < id->generic_param_count; i += 1) {
+    //    GenericParamValue *generic_param = &id->generic_params[i];
+    //    if (generic_param->node) {
+    //        ConstExprValue *const_val = &get_resolved_expr(generic_param->node)->instruction->static_value;
+    //        assert(const_val->special != ConstValSpecialRuntime);
+    //        result += hash_const_val(generic_param->type, const_val);
+    //    }
+    //    result += hash_ptr(generic_param->type);
+    //}
+    //return result;
 }
 
 bool generic_fn_type_id_eql(GenericFnTypeId *a, GenericFnTypeId *b) {
-    if (a->decl_node != b->decl_node) return false;
-    assert(a->generic_param_count == b->generic_param_count);
-    for (size_t i = 0; i < a->generic_param_count; i += 1) {
-        GenericParamValue *a_val = &a->generic_params[i];
-        GenericParamValue *b_val = &b->generic_params[i];
-        if (a_val->type != b_val->type) return false;
-        if (a_val->node && b_val->node) {
-            ConstExprValue *a_const_val = &get_resolved_expr(a_val->node)->instruction->static_value;
-            ConstExprValue *b_const_val = &get_resolved_expr(b_val->node)->instruction->static_value;
-            assert(a_const_val->special != ConstValSpecialRuntime);
-            assert(b_const_val->special != ConstValSpecialRuntime);
-            if (!const_values_equal(a_const_val, b_const_val, a_val->type)) {
-                return false;
-            }
-        } else {
-            assert(!a_val->node && !b_val->node);
-        }
-    }
-    return true;
+    zig_panic("TODO generic_fn_type_id_eql");
+    //if (a->decl_node != b->decl_node) return false;
+    //assert(a->generic_param_count == b->generic_param_count);
+    //for (size_t i = 0; i < a->generic_param_count; i += 1) {
+    //    GenericParamValue *a_val = &a->generic_params[i];
+    //    GenericParamValue *b_val = &b->generic_params[i];
+    //    if (a_val->type != b_val->type) return false;
+    //    if (a_val->node && b_val->node) {
+    //        ConstExprValue *a_const_val = &get_resolved_expr(a_val->node)->instruction->static_value;
+    //        ConstExprValue *b_const_val = &get_resolved_expr(b_val->node)->instruction->static_value;
+    //        assert(a_const_val->special != ConstValSpecialRuntime);
+    //        assert(b_const_val->special != ConstValSpecialRuntime);
+    //        if (!const_values_equal(a_const_val, b_const_val, a_val->type)) {
+    //            return false;
+    //        }
+    //    } else {
+    //        assert(!a_val->node && !b_val->node);
+    //    }
+    //}
+    //return true;
 }
 
 bool type_has_bits(TypeTableEntry *type_entry) {
@@ -3095,5 +3002,3 @@ uint64_t get_memcpy_align(CodeGen *g, TypeTableEntry *type_entry) {
     TypeTableEntry *first_type_in_mem = type_of_first_thing_in_memory(type_entry);
     return LLVMABISizeOfType(g->target_data_ref, first_type_in_mem->type_ref);
 }
-
-
src/analyze.hpp
@@ -16,7 +16,6 @@ ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *m
 TypeTableEntry *new_type_table_entry(TypeTableEntryId id);
 TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const);
 BlockContext *new_block_context(AstNode *node, BlockContext *parent);
-Expr *get_resolved_expr(AstNode *node);
 bool is_node_void_expr(AstNode *node);
 uint64_t type_size(CodeGen *g, TypeTableEntry *type_entry);
 TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, size_t size_in_bits);
@@ -63,5 +62,8 @@ TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name);
 BlockContext *get_container_block_context(TypeTableEntry *type_entry);
 TypeEnumField *find_enum_type_field(TypeTableEntry *enum_type, Buf *name);
 bool is_container_ref(TypeTableEntry *type_entry);
+void scan_decls(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node);
+void preview_use_decl(CodeGen *g, AstNode *node);
+void resolve_use_decl(CodeGen *g, AstNode *node);
 
 #endif
src/ast_render.cpp
@@ -528,25 +528,24 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
                 break;
             }
         case NodeTypeFnCallExpr:
-            if (node->data.fn_call_expr.is_builtin) {
-                fprintf(ar->f, "@");
-            } else {
+            {
+                if (node->data.fn_call_expr.is_builtin) {
+                    fprintf(ar->f, "@");
+                }
+                AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr;
+                bool grouped = (fn_ref_node->type != NodeTypeBinOpExpr);
+                render_node_extra(ar, fn_ref_node, grouped);
                 fprintf(ar->f, "(");
-            }
-            render_node_ungrouped(ar, node->data.fn_call_expr.fn_ref_expr);
-            if (!node->data.fn_call_expr.is_builtin) {
-                fprintf(ar->f, ")");
-            }
-            fprintf(ar->f, "(");
-            for (size_t i = 0; i < node->data.fn_call_expr.params.length; i += 1) {
-                AstNode *param = node->data.fn_call_expr.params.at(i);
-                if (i != 0) {
-                    fprintf(ar->f, ", ");
+                for (size_t i = 0; i < node->data.fn_call_expr.params.length; i += 1) {
+                    AstNode *param = node->data.fn_call_expr.params.at(i);
+                    if (i != 0) {
+                        fprintf(ar->f, ", ");
+                    }
+                    render_node_grouped(ar, param);
                 }
-                render_node_grouped(ar, param);
+                fprintf(ar->f, ")");
+                break;
             }
-            fprintf(ar->f, ")");
-            break;
         case NodeTypeArrayAccessExpr:
             render_node_ungrouped(ar, node->data.array_access_expr.array_ref_expr);
             fprintf(ar->f, "[");
src/codegen.cpp
@@ -65,8 +65,6 @@ CodeGen *codegen_create(Buf *root_source_dir, const ZigTarget *target) {
     g->is_test_build = false;
     g->want_h_file = true;
 
-    g->len_buf = buf_create_from_str("len");
-
     // the error.Ok value
     g->error_decls.append(nullptr);
 
@@ -1682,6 +1680,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
         case IrInstructionIdSizeOf:
         case IrInstructionIdSwitchTarget:
         case IrInstructionIdStaticEval:
+        case IrInstructionIdImport:
             zig_unreachable();
         case IrInstructionIdReturn:
             return ir_render_return(g, executable, (IrInstructionReturn *)instruction);
@@ -1728,8 +1727,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
         case IrInstructionIdSwitchVar:
         case IrInstructionIdContainerInitList:
         case IrInstructionIdContainerInitFields:
-        case IrInstructionIdReadField:
         case IrInstructionIdEnumTag:
+        case IrInstructionIdArrayLen:
             zig_panic("TODO render more IR instructions to LLVM");
     }
     zig_unreachable();
@@ -2114,7 +2113,7 @@ static void do_code_gen(CodeGen *g) {
 
         if (var->type->id == TypeTableEntryIdNumLitFloat) {
             // Generate debug info for it but that's it.
-            ConstExprValue *const_val = &get_resolved_expr(var->val_node)->instruction->static_value;
+            ConstExprValue *const_val = &var->decl_node->data.variable_declaration.top_level_decl.value->static_value;
             assert(const_val->special != ConstValSpecialRuntime);
             TypeTableEntry *var_type = g->builtin_types.entry_f64;
             LLVMValueRef init_val = LLVMConstReal(var_type->type_ref, const_val->data.x_bignum.data.x_float);
@@ -2124,7 +2123,7 @@ static void do_code_gen(CodeGen *g) {
 
         if (var->type->id == TypeTableEntryIdNumLitInt) {
             // Generate debug info for it but that's it.
-            ConstExprValue *const_val = &get_resolved_expr(var->val_node)->instruction->static_value;
+            ConstExprValue *const_val = &var->decl_node->data.variable_declaration.top_level_decl.value->static_value;
             assert(const_val->special != ConstValSpecialRuntime);
             TypeTableEntry *var_type = const_val->data.x_bignum.is_negative ?
                 g->builtin_types.entry_isize : g->builtin_types.entry_usize;
@@ -2149,8 +2148,7 @@ static void do_code_gen(CodeGen *g) {
 
             LLVMSetLinkage(global_value, LLVMExternalLinkage);
         } else {
-            AstNode *expr_node = var->decl_node->data.variable_declaration.expr;
-            IrInstruction *instruction = get_resolved_expr(expr_node)->instruction;
+            IrInstruction *instruction = var->decl_node->data.variable_declaration.top_level_decl.value;
             render_const_val(g, instruction->type_entry, &instruction->static_value);
             render_const_val_global(g, instruction->type_entry, &instruction->static_value);
             global_value = instruction->static_value.llvm_global;
src/ir.cpp
@@ -3,6 +3,7 @@
 #include "eval.hpp"
 #include "ir.hpp"
 #include "ir_print.hpp"
+#include "os.hpp"
 
 struct IrExecContext {
     ConstExprValue *mem_slot_list;
@@ -159,10 +160,6 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionStructFieldPtr *
     return IrInstructionIdStructFieldPtr;
 }
 
-static constexpr IrInstructionId ir_instruction_id(IrInstructionReadField *) {
-    return IrInstructionIdReadField;
-}
-
 static constexpr IrInstructionId ir_instruction_id(IrInstructionElemPtr *) {
     return IrInstructionIdElemPtr;
 }
@@ -267,6 +264,14 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionStaticEval *) {
     return IrInstructionIdStaticEval;
 }
 
+static constexpr IrInstructionId ir_instruction_id(IrInstructionImport *) {
+    return IrInstructionIdImport;
+}
+
+static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayLen *) {
+    return IrInstructionIdArrayLen;
+}
+
 template<typename T>
 static T *ir_create_instruction(IrExecutable *exec, AstNode *source_node) {
     T *special_instruction = allocate<T>(1);
@@ -565,18 +570,6 @@ static IrInstruction *ir_build_field_ptr(IrBuilder *irb, AstNode *source_node,
     return &instruction->base;
 }
 
-static IrInstruction *ir_build_read_field(IrBuilder *irb, AstNode *source_node,
-    IrInstruction *container_ptr, Buf *field_name)
-{
-    IrInstructionReadField *instruction = ir_build_instruction<IrInstructionReadField>(irb, source_node);
-    instruction->container_ptr = container_ptr;
-    instruction->field_name = field_name;
-
-    ir_ref_instruction(container_ptr);
-
-    return &instruction->base;
-}
-
 static IrInstruction *ir_build_struct_field_ptr(IrBuilder *irb, AstNode *source_node,
     IrInstruction *struct_ptr, TypeStructField *field)
 {
@@ -1087,6 +1080,32 @@ static IrInstruction *ir_build_static_eval(IrBuilder *irb, AstNode *source_node,
     return &instruction->base;
 }
 
+static IrInstruction *ir_build_import(IrBuilder *irb, AstNode *source_node, IrInstruction *name) {
+    IrInstructionImport *instruction = ir_build_instruction<IrInstructionImport>(irb, source_node);
+    instruction->name = name;
+
+    ir_ref_instruction(name);
+
+    return &instruction->base;
+}
+
+static IrInstruction *ir_build_array_len(IrBuilder *irb, AstNode *source_node, IrInstruction *array_value) {
+    IrInstructionArrayLen *instruction = ir_build_instruction<IrInstructionArrayLen>(irb, source_node);
+    instruction->array_value = array_value;
+
+    ir_ref_instruction(array_value);
+
+    return &instruction->base;
+}
+
+static IrInstruction *ir_build_array_len_from(IrBuilder *irb, IrInstruction *old_instruction,
+        IrInstruction *array_value)
+{
+    IrInstruction *new_instruction = ir_build_array_len(irb, old_instruction->source_node, array_value);
+    ir_link_new_instruction(new_instruction, old_instruction);
+    return new_instruction;
+}
+
 static void ir_gen_defers_for_block(IrBuilder *irb, BlockContext *inner_block, BlockContext *outer_block,
         bool gen_error_defers, bool gen_maybe_defers)
 {
@@ -1376,7 +1395,7 @@ static IrInstruction *ir_gen_null_literal(IrBuilder *irb, AstNode *node) {
 static IrInstruction *ir_gen_decl_ref(IrBuilder *irb, AstNode *source_node, AstNode *decl_node,
         LValPurpose lval, BlockContext *scope)
 {
-    resolve_top_level_decl(irb->codegen, decl_node, lval);
+    resolve_top_level_decl(irb->codegen, decl_node, lval != LValPurposeNone);
     TopLevelDecl *tld = get_as_top_level_decl(decl_node);
     if (tld->resolution == TldResolutionInvalid)
         return irb->codegen->invalid_instruction;
@@ -1492,15 +1511,16 @@ static IrInstruction *ir_gen_field_access(IrBuilder *irb, AstNode *node, LValPur
     AstNode *container_ref_node = node->data.field_access_expr.struct_expr;
     Buf *field_name = node->data.field_access_expr.field_name;
 
-    IrInstruction *container_ref_instruction = ir_gen_node(irb, container_ref_node, node->block_context);
+    IrInstruction *container_ref_instruction = ir_gen_node_extra(irb, container_ref_node, node->block_context,
+            LValPurposeAddressOf);
     if (container_ref_instruction == irb->codegen->invalid_instruction)
         return container_ref_instruction;
 
-    if (lval == LValPurposeNone) {
-        return ir_build_read_field(irb, node, container_ref_instruction, field_name);
-    } else {
-        return ir_build_field_ptr(irb, node, container_ref_instruction, field_name);
-    }
+    IrInstruction *ptr_instruction = ir_build_field_ptr(irb, node, container_ref_instruction, field_name);
+    if (lval != LValPurposeNone)
+        return ptr_instruction;
+
+    return ir_build_load_ptr(irb, node, ptr_instruction);
 }
 
 static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, AstNode *node) {
@@ -1628,6 +1648,20 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, AstNode *node) {
 
                 return ir_build_static_eval(irb, node, arg0_value);
             }
+        case BuiltinFnIdImport:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, node->block_context);
+                if (arg0_value == irb->codegen->invalid_instruction)
+                    return arg0_value;
+
+                if (node->block_context->fn_entry) {
+                    add_node_error(irb->codegen, node, buf_sprintf("import valid only at top level scope"));
+                    return irb->codegen->invalid_instruction;
+                }
+
+                return ir_build_import(irb, node, arg0_value);
+            }
         case BuiltinFnIdMemcpy:
         case BuiltinFnIdMemset:
         case BuiltinFnIdAlignof:
@@ -1642,7 +1676,6 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, AstNode *node) {
         case BuiltinFnIdCDefine:
         case BuiltinFnIdCUndef:
         case BuiltinFnIdCompileErr:
-        case BuiltinFnIdImport:
         case BuiltinFnIdCImport:
         case BuiltinFnIdErrName:
         case BuiltinFnIdBreakpoint:
@@ -1978,7 +2011,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, AstNode *node) {
     IrBasicBlock *end_block = ir_build_basic_block(irb, "ForEnd");
     IrBasicBlock *continue_block = ir_build_basic_block(irb, "ForContinue");
 
-    IrInstruction *len_val = ir_build_read_field(irb, node, array_val, irb->codegen->len_buf);
+    IrInstruction *len_val = ir_build_array_len(irb, node, array_val);
     ir_build_br(irb, node, cond_block, is_inline);
 
     ir_set_cursor_at_end(irb, cond_block);
@@ -2846,7 +2879,9 @@ static ConstExprValue *ir_build_const_from(IrAnalyze *ira, IrInstruction *old_in
         var_ptr_instruction->var = old_var_ptr_instruction->var;
         new_instruction = &var_ptr_instruction->base;
     } else if (old_instruction->id == IrInstructionIdFieldPtr) {
-        zig_panic("TODO");
+        IrInstructionFieldPtr *field_ptr_instruction = ir_create_instruction<IrInstructionFieldPtr>(ira->new_irb.exec,
+                old_instruction->source_node);
+        new_instruction = &field_ptr_instruction->base;
     } else if (old_instruction->id == IrInstructionIdElemPtr) {
         IrInstructionElemPtr *elem_ptr_instruction = ir_create_instruction<IrInstructionElemPtr>(ira->new_irb.exec,
                 old_instruction->source_node);
@@ -2868,6 +2903,17 @@ static TypeTableEntry *ir_analyze_void(IrAnalyze *ira, IrInstruction *instructio
     return ira->codegen->builtin_types.entry_void;
 }
 
+static TypeTableEntry *ir_analyze_const_ptr(IrAnalyze *ira, IrInstruction *instruction,
+        ConstExprValue *pointee, TypeTableEntry *pointee_type, bool depends_on_compile_var)
+{
+    TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, pointee_type, true);
+    ConstExprValue *const_val = ir_build_const_from(ira, instruction,
+            depends_on_compile_var || pointee->depends_on_compile_var);
+    const_val->data.x_ptr.base_ptr = pointee;
+    const_val->data.x_ptr.index = SIZE_MAX;
+    return ptr_type;
+}
+
 static TypeTableEntry *ir_analyze_const_usize(IrAnalyze *ira, IrInstruction *instruction, uint64_t value,
     bool depends_on_compile_var)
 {
@@ -2889,9 +2935,6 @@ static TypeTableEntry *ir_resolve_type_lval(IrAnalyze *ira, IrInstruction *type_
     if (lval != LValPurposeNone)
         zig_panic("TODO");
 
-    if (type_value == ira->codegen->invalid_instruction)
-        return ira->codegen->builtin_types.entry_invalid;
-
     if (type_value->type_entry->id == TypeTableEntryIdInvalid)
         return ira->codegen->builtin_types.entry_invalid;
 
@@ -3810,7 +3853,7 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction
             }
 
             ir_build_call_from(&ira->new_irb, &call_instruction->base,
-                    call_instruction->fn, call_instruction->arg_count, casted_args);
+                    fn_ref, call_instruction->arg_count, casted_args);
 
             return ir_finish_anal(ira, fn_type->data.fn.fn_type_id.return_type);
         } else {
@@ -4286,14 +4329,11 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP
     return resolved_type;
 }
 
-static TypeTableEntry *ir_analyze_instruction_var_ptr(IrAnalyze *ira, IrInstructionVarPtr *var_ptr_instruction) {
-    VariableTableEntry *var = var_ptr_instruction->var;
+static TypeTableEntry *ir_analyze_var_ptr(IrAnalyze *ira, IrInstruction *instruction, VariableTableEntry *var) {
     assert(var->type);
     if (var->type->id == TypeTableEntryIdInvalid)
         return var->type;
 
-    TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, var->type, false);
-
     ConstExprValue *mem_slot = nullptr;
     if (var->block_context->fn_entry) {
         // TODO once the analyze code is fully ported over to IR we won't need this SIZE_MAX thing.
@@ -4302,23 +4342,23 @@ static TypeTableEntry *ir_analyze_instruction_var_ptr(IrAnalyze *ira, IrInstruct
     } else if (var->src_is_const) {
         AstNode *var_decl_node = var->decl_node;
         assert(var_decl_node->type == NodeTypeVariableDeclaration);
-        mem_slot = &get_resolved_expr(var_decl_node->data.variable_declaration.expr)->instruction->static_value;
+        mem_slot = &var_decl_node->data.variable_declaration.top_level_decl.value->static_value;
         assert(mem_slot->special != ConstValSpecialRuntime);
     }
 
     if (mem_slot && mem_slot->special != ConstValSpecialRuntime) {
-        ConstExprValue *out_val = ir_build_const_from(ira, &var_ptr_instruction->base,
-                mem_slot->depends_on_compile_var);
-
-        out_val->data.x_ptr.base_ptr = mem_slot;
-        out_val->data.x_ptr.index = SIZE_MAX;
-        return ptr_type;
+        return ir_analyze_const_ptr(ira, instruction, mem_slot, var->type, false);
     } else {
-        ir_build_var_ptr_from(&ira->new_irb, &var_ptr_instruction->base, var);
-        return ptr_type;
+        ir_build_var_ptr_from(&ira->new_irb, instruction, var);
+        return get_pointer_to_type(ira->codegen, var->type, false);
     }
 }
 
+static TypeTableEntry *ir_analyze_instruction_var_ptr(IrAnalyze *ira, IrInstructionVarPtr *var_ptr_instruction) {
+    VariableTableEntry *var = var_ptr_instruction->var;
+    return ir_analyze_var_ptr(ira, &var_ptr_instruction->base, var);
+}
+
 static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstructionElemPtr *elem_ptr_instruction) {
     IrInstruction *array_ptr = elem_ptr_instruction->array_ptr->other;
     if (array_ptr->type_entry->id == TypeTableEntryIdInvalid)
@@ -4483,108 +4523,146 @@ static TypeTableEntry *ir_analyze_container_member_access(IrAnalyze *ira, Buf *f
     }
 }
 
-static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstructionFieldPtr *field_ptr_instruction) {
-    IrInstruction *container_ptr = field_ptr_instruction->container_ptr->other;
-    Buf *field_name = field_ptr_instruction->field_name;
+static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source_instruction, AstNode *decl_node,
+        bool depends_on_compile_var)
+{
+    bool pointer_only = false;
+    resolve_top_level_decl(ira->codegen, decl_node, pointer_only);
+    TopLevelDecl *tld = get_as_top_level_decl(decl_node);
+    if (tld->resolution == TldResolutionInvalid)
+        return ira->codegen->builtin_types.entry_invalid;
 
-    TypeTableEntry *container_type = container_ptr->type_entry;
-    if (container_type->id == TypeTableEntryIdInvalid) {
-        return container_type;
-    } else if (is_container_ref(container_type)) {
-        return ir_analyze_container_member_access(ira, field_name, field_ptr_instruction, container_type);
-    } else if (container_type->id == TypeTableEntryIdArray) {
-        if (buf_eql_str(field_name, "len")) {
-            add_node_error(ira->codegen, field_ptr_instruction->base.source_node,
-                buf_sprintf("pointer to array length not available"));
-            return ira->codegen->builtin_types.entry_invalid;
-        } else {
-            add_node_error(ira->codegen, field_ptr_instruction->base.source_node,
-                buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name),
-                    buf_ptr(&container_type->name)));
-            return ira->codegen->builtin_types.entry_invalid;
-        }
-    } else if (container_type->id == TypeTableEntryIdMetaType) {
-        TypeTableEntry *child_type = ir_resolve_type(ira, container_ptr);
+    if (decl_node->type == NodeTypeVariableDeclaration) {
+        VariableTableEntry *var = decl_node->data.variable_declaration.variable;
+        return ir_analyze_var_ptr(ira, source_instruction, var);
+    } else if (decl_node->type == NodeTypeFnProto) {
+        FnTableEntry *fn_entry = decl_node->data.fn_proto.fn_table_entry;
+        assert(fn_entry->type_entry);
 
-        if (child_type->id == TypeTableEntryIdInvalid) {
-            return ira->codegen->builtin_types.entry_invalid;
-        } else if (child_type->id == TypeTableEntryIdEnum) {
-            zig_panic("TODO enum type field");
-        } else if (child_type->id == TypeTableEntryIdStruct) {
-            zig_panic("TODO struct type field");
-        } else if (child_type->id == TypeTableEntryIdPureError) {
-            zig_panic("TODO error type field");
-        } else if (child_type->id == TypeTableEntryIdInt) {
-            zig_panic("TODO integer type field");
+        // TODO instead of allocating this every time, put it in the tld value and we can reference
+        // the same one every time
+        ConstExprValue *const_val = allocate<ConstExprValue>(1);
+        const_val->special = ConstValSpecialStatic;
+        if (fn_entry->type_entry->id == TypeTableEntryIdGenericFn) {
+            const_val->data.x_type = fn_entry->type_entry;
         } else {
-            add_node_error(ira->codegen, field_ptr_instruction->base.source_node,
-                buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name)));
-            return ira->codegen->builtin_types.entry_invalid;
+            const_val->data.x_fn = fn_entry;
         }
-    } else if (container_type->id == TypeTableEntryIdNamespace) {
-        zig_panic("TODO namespace field access");
+
+        return ir_analyze_const_ptr(ira, source_instruction, const_val, fn_entry->type_entry, depends_on_compile_var);
+    } else if (decl_node->type == NodeTypeContainerDecl) {
+        zig_panic("TODO");
+        //ConstExprValue *out_val = ir_build_const_from(ira, source_instruction, depends_on_compile_var);
+        //if (decl_node->data.struct_decl.generic_params.length > 0) {
+        //    TypeTableEntry *type_entry = decl_node->data.struct_decl.generic_fn_type;
+        //    assert(type_entry);
+        //    out_val->data.x_type = type_entry;
+        //    return type_entry;
+        //} else {
+        //    out_val->data.x_type = decl_node->data.struct_decl.type_entry;
+        //    return ira->codegen->builtin_types.entry_type;
+        //}
+    } else if (decl_node->type == NodeTypeTypeDecl) {
+        zig_panic("TODO");
+        //ConstExprValue *out_val = ir_build_const_from(ira, source_instruction, depends_on_compile_var);
+        //out_val->data.x_type = decl_node->data.type_decl.child_type_entry;
+        //return ira->codegen->builtin_types.entry_type;
     } else {
-        add_node_error(ira->codegen, field_ptr_instruction->base.source_node,
-            buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name)));
-        return ira->codegen->builtin_types.entry_invalid;
+        zig_unreachable();
     }
 }
 
-static TypeTableEntry *ir_analyze_read_field_as_ptr_load(IrAnalyze *ira,
-    IrInstructionReadField *read_field_instruction)
-{
-    IrInstruction *old_field_ptr_inst = ir_build_field_ptr(&ira->old_irb, read_field_instruction->base.source_node,
-        read_field_instruction->container_ptr, read_field_instruction->field_name);
-    IrInstruction *old_load_ptr_inst = ir_build_load_ptr(&ira->old_irb, read_field_instruction->base.source_node,
-        old_field_ptr_inst);
-    ir_analyze_instruction(ira, old_field_ptr_inst);
-    TypeTableEntry *result_type = ir_analyze_instruction(ira, old_load_ptr_inst);
-    read_field_instruction->base.other = old_load_ptr_inst->other;
-    return result_type;
-}
+static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstructionFieldPtr *field_ptr_instruction) {
+    IrInstruction *container_ptr = field_ptr_instruction->container_ptr->other;
+    if (container_ptr->type_entry->id == TypeTableEntryIdInvalid)
+        return ira->codegen->builtin_types.entry_invalid;
 
-static TypeTableEntry *ir_analyze_instruction_read_field(IrAnalyze *ira,
-    IrInstructionReadField *read_field_instruction)
-{
-    IrInstruction *container_ptr = read_field_instruction->container_ptr->other;
-    Buf *field_name = read_field_instruction->field_name;
+    assert(container_ptr->type_entry->id == TypeTableEntryIdPointer);
+    TypeTableEntry *container_type = container_ptr->type_entry->data.pointer.child_type;
+
+    Buf *field_name = field_ptr_instruction->field_name;
+    AstNode *source_node = field_ptr_instruction->base.source_node;
 
-    TypeTableEntry *container_type = container_ptr->type_entry;
     if (container_type->id == TypeTableEntryIdInvalid) {
         return container_type;
     } else if (is_container_ref(container_type)) {
-        return ir_analyze_read_field_as_ptr_load(ira, read_field_instruction);
+        return ir_analyze_container_member_access(ira, field_name, field_ptr_instruction, container_type);
     } else if (container_type->id == TypeTableEntryIdArray) {
         if (buf_eql_str(field_name, "len")) {
-            return ir_analyze_const_usize(ira, &read_field_instruction->base, container_type->data.array.len, false);
+            ConstExprValue *len_val = allocate<ConstExprValue>(1);
+            len_val->special = ConstValSpecialStatic;
+            bignum_init_unsigned(&len_val->data.x_bignum, container_type->data.array.len);
+
+            TypeTableEntry *usize = ira->codegen->builtin_types.entry_usize;
+            return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, len_val, usize, false);
         } else {
-            add_node_error(ira->codegen, read_field_instruction->base.source_node,
+            add_node_error(ira->codegen, source_node,
                 buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name),
                     buf_ptr(&container_type->name)));
             return ira->codegen->builtin_types.entry_invalid;
         }
     } else if (container_type->id == TypeTableEntryIdMetaType) {
-        TypeTableEntry *child_type = ir_resolve_type(ira, container_ptr);
-
-        if (child_type->id == TypeTableEntryIdInvalid) {
+        zig_panic("TODO type field access");
+        //TypeTableEntry *child_type = ir_resolve_type(ira, container_ptr);
+
+        //if (child_type->id == TypeTableEntryIdInvalid) {
+        //    return ira->codegen->builtin_types.entry_invalid;
+        //} else if (child_type->id == TypeTableEntryIdEnum) {
+        //    zig_panic("TODO enum type field");
+        //} else if (child_type->id == TypeTableEntryIdStruct) {
+        //    zig_panic("TODO struct type field");
+        //} else if (child_type->id == TypeTableEntryIdPureError) {
+        //    zig_panic("TODO error type field");
+        //} else if (child_type->id == TypeTableEntryIdInt) {
+        //    zig_panic("TODO integer type field");
+        //} else {
+        //    add_node_error(ira->codegen, source_node,
+        //        buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name)));
+        //    return ira->codegen->builtin_types.entry_invalid;
+        //}
+    } else if (container_type->id == TypeTableEntryIdNamespace) {
+        ConstExprValue *container_ptr_val = ir_resolve_const(ira, container_ptr);
+        if (!container_ptr_val)
             return ira->codegen->builtin_types.entry_invalid;
-        } else if (child_type->id == TypeTableEntryIdEnum) {
-            zig_panic("TODO enum type field");
-        } else if (child_type->id == TypeTableEntryIdStruct) {
-            zig_panic("TODO struct type field");
-        } else if (child_type->id == TypeTableEntryIdPureError) {
-            zig_panic("TODO error type field");
-        } else if (child_type->id == TypeTableEntryIdInt) {
-            zig_panic("TODO integer type field");
+
+        ConstExprValue *namespace_val = const_ptr_pointee(container_ptr_val);
+        assert(namespace_val->special == ConstValSpecialStatic);
+
+        ImportTableEntry *namespace_import = namespace_val->data.x_import;
+
+        bool depends_on_compile_var = container_ptr->static_value.depends_on_compile_var;
+        AstNode *decl_node = find_decl(namespace_import->block_context, field_name);
+        if (!decl_node) {
+            // we must now resolve all the use decls
+            for (size_t i = 0; i < namespace_import->use_decls.length; i += 1) {
+                AstNode *use_decl_node = namespace_import->use_decls.at(i);
+                TopLevelDecl *tld = get_as_top_level_decl(use_decl_node);
+                if (tld->resolution == TldResolutionUnresolved) {
+                    preview_use_decl(ira->codegen, use_decl_node);
+                }
+                resolve_use_decl(ira->codegen, use_decl_node);
+            }
+            decl_node = find_decl(namespace_import->block_context, field_name);
+        }
+        if (decl_node) {
+            TopLevelDecl *tld = get_as_top_level_decl(decl_node);
+            if (tld->visib_mod == VisibModPrivate &&
+                decl_node->owner != source_node->owner)
+            {
+                ErrorMsg *msg = add_node_error(ira->codegen, source_node,
+                    buf_sprintf("'%s' is private", buf_ptr(field_name)));
+                add_error_note(ira->codegen, msg, decl_node, buf_sprintf("declared here"));
+                return ira->codegen->builtin_types.entry_invalid;
+            }
+            return ir_analyze_decl_ref(ira, &field_ptr_instruction->base, decl_node, depends_on_compile_var);
         } else {
-            add_node_error(ira->codegen, read_field_instruction->base.source_node,
-                buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name)));
+            const char *import_name = namespace_import->path ? buf_ptr(namespace_import->path) : "(C import)";
+            add_node_error(ira->codegen, source_node,
+                buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), import_name));
             return ira->codegen->builtin_types.entry_invalid;
         }
-    } else if (container_type->id == TypeTableEntryIdNamespace) {
-        zig_panic("TODO namespace field access");
     } else {
-        add_node_error(ira->codegen, read_field_instruction->base.source_node,
+        add_node_error(ira->codegen, field_ptr_instruction->base.source_node,
             buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name)));
         return ira->codegen->builtin_types.entry_invalid;
     }
@@ -4610,7 +4688,7 @@ static TypeTableEntry *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstruc
         return child_type;
     } else {
         add_node_error(ira->codegen, load_ptr_instruction->base.source_node,
-            buf_sprintf("indirection requires pointer operand ('%s' invalid)",
+            buf_sprintf("attempt to dereference non pointer type '%s'",
                 buf_ptr(&type_entry->name)));
         return ira->codegen->builtin_types.entry_invalid;
     }
@@ -5413,6 +5491,110 @@ static TypeTableEntry *ir_analyze_instruction_static_eval(IrAnalyze *ira,
     return value->type_entry;
 }
 
+static TypeTableEntry *ir_analyze_instruction_import(IrAnalyze *ira, IrInstructionImport *import_instruction) {
+    IrInstruction *name_value = import_instruction->name->other;
+    Buf *import_target_str = ir_resolve_str(ira, name_value);
+    if (!import_target_str)
+        return ira->codegen->builtin_types.entry_invalid;
+    bool depends_on_compile_var = name_value->static_value.depends_on_compile_var;
+
+    AstNode *source_node = import_instruction->base.source_node;
+    ImportTableEntry *import = source_node->owner;
+
+    Buf *import_target_path;
+    Buf *search_dir;
+    assert(import->package);
+    PackageTableEntry *target_package;
+    auto package_entry = import->package->package_table.maybe_get(import_target_str);
+    if (package_entry) {
+        target_package = package_entry->value;
+        import_target_path = &target_package->root_src_path;
+        search_dir = &target_package->root_src_dir;
+    } else {
+        // try it as a filename
+        target_package = import->package;
+        import_target_path = import_target_str;
+        search_dir = &import->package->root_src_dir;
+    }
+
+    Buf full_path = BUF_INIT;
+    os_path_join(search_dir, import_target_path, &full_path);
+
+    Buf *import_code = buf_alloc();
+    Buf *abs_full_path = buf_alloc();
+    int err;
+    if ((err = os_path_real(&full_path, abs_full_path))) {
+        if (err == ErrorFileNotFound) {
+            add_node_error(ira->codegen, source_node,
+                    buf_sprintf("unable to find '%s'", buf_ptr(import_target_path)));
+            return ira->codegen->builtin_types.entry_invalid;
+        } else {
+            ira->codegen->error_during_imports = true;
+            add_node_error(ira->codegen, source_node,
+                    buf_sprintf("unable to open '%s': %s", buf_ptr(&full_path), err_str(err)));
+            return ira->codegen->builtin_types.entry_invalid;
+        }
+    }
+
+    auto import_entry = ira->codegen->import_table.maybe_get(abs_full_path);
+    if (import_entry) {
+        ConstExprValue *out_val = ir_build_const_from(ira, &import_instruction->base, depends_on_compile_var);
+        out_val->data.x_import = import_entry->value;
+        return ira->codegen->builtin_types.entry_namespace;
+    }
+
+    if ((err = os_fetch_file_path(abs_full_path, import_code))) {
+        if (err == ErrorFileNotFound) {
+            add_node_error(ira->codegen, source_node,
+                    buf_sprintf("unable to find '%s'", buf_ptr(import_target_path)));
+            return ira->codegen->builtin_types.entry_invalid;
+        } else {
+            add_node_error(ira->codegen, source_node,
+                    buf_sprintf("unable to open '%s': %s", buf_ptr(&full_path), err_str(err)));
+            return ira->codegen->builtin_types.entry_invalid;
+        }
+    }
+    ImportTableEntry *target_import = add_source_file(ira->codegen, target_package,
+            abs_full_path, search_dir, import_target_path, import_code);
+
+    scan_decls(ira->codegen, target_import, target_import->block_context, target_import->root);
+
+    ConstExprValue *out_val = ir_build_const_from(ira, &import_instruction->base, depends_on_compile_var);
+    out_val->data.x_import = target_import;
+    return ira->codegen->builtin_types.entry_namespace;
+
+}
+
+static TypeTableEntry *ir_analyze_instruction_array_len(IrAnalyze *ira,
+        IrInstructionArrayLen *array_len_instruction)
+{
+    IrInstruction *array_value = array_len_instruction->array_value->other;
+    TypeTableEntry *canon_type = get_underlying_type(array_value->type_entry);
+    if (canon_type->id == TypeTableEntryIdInvalid) {
+        return ira->codegen->builtin_types.entry_invalid;
+    } else if (canon_type->id == TypeTableEntryIdArray) {
+        bool depends_on_compile_var = array_value->static_value.depends_on_compile_var;
+        return ir_analyze_const_usize(ira, &array_len_instruction->base,
+                canon_type->data.array.len, depends_on_compile_var);
+    } else if (is_slice(canon_type)) {
+        if (array_value->static_value.special != ConstValSpecialRuntime) {
+            ConstExprValue *len_val = &array_value->static_value.data.x_struct.fields[slice_len_index];
+            if (len_val->special != ConstValSpecialRuntime) {
+                bool depends_on_compile_var = len_val->depends_on_compile_var;
+                return ir_analyze_const_usize(ira, &array_len_instruction->base,
+                        len_val->data.x_bignum.data.x_uint, depends_on_compile_var);
+            }
+        }
+        ir_build_array_len_from(&ira->new_irb, &array_len_instruction->base, array_value);
+        return ira->codegen->builtin_types.entry_usize;
+    } else {
+        add_node_error(ira->codegen, array_len_instruction->base.source_node,
+            buf_sprintf("type '%s' has no field 'len'", buf_ptr(&array_value->type_entry->name)));
+        // TODO if this is a typedecl, add error note showing the declaration of the type decl
+        return ira->codegen->builtin_types.entry_invalid;
+    }
+}
+
 static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
     switch (instruction->id) {
         case IrInstructionIdInvalid:
@@ -5437,8 +5619,6 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
             return ir_analyze_instruction_var_ptr(ira, (IrInstructionVarPtr *)instruction);
         case IrInstructionIdFieldPtr:
             return ir_analyze_instruction_field_ptr(ira, (IrInstructionFieldPtr *)instruction);
-        case IrInstructionIdReadField:
-            return ir_analyze_instruction_read_field(ira, (IrInstructionReadField *)instruction);
         case IrInstructionIdCall:
             return ir_analyze_instruction_call(ira, (IrInstructionCall *)instruction);
         case IrInstructionIdBr:
@@ -5489,6 +5669,10 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
             return ir_analyze_instruction_enum_tag(ira, (IrInstructionEnumTag *)instruction);
         case IrInstructionIdStaticEval:
             return ir_analyze_instruction_static_eval(ira, (IrInstructionStaticEval *)instruction);
+        case IrInstructionIdImport:
+            return ir_analyze_instruction_import(ira, (IrInstructionImport *)instruction);
+        case IrInstructionIdArrayLen:
+            return ir_analyze_instruction_array_len(ira, (IrInstructionArrayLen *)instruction);
         case IrInstructionIdCast:
         case IrInstructionIdContainerInitList:
         case IrInstructionIdContainerInitFields:
@@ -5580,6 +5764,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
         case IrInstructionIdSetFnTest:
         case IrInstructionIdSetFnVisible:
         case IrInstructionIdSetDebugSafety:
+        case IrInstructionIdImport:
             return true;
         case IrInstructionIdPhi:
         case IrInstructionIdUnOp:
@@ -5595,7 +5780,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
         case IrInstructionIdTypeOf:
         case IrInstructionIdToPtrType:
         case IrInstructionIdPtrTypeChild:
-        case IrInstructionIdReadField:
+        case IrInstructionIdArrayLen:
         case IrInstructionIdStructFieldPtr:
         case IrInstructionIdArrayType:
         case IrInstructionIdSliceType:
@@ -5666,81 +5851,6 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
 //    }
 //}
 
-//static TypeTableEntry *analyze_import(CodeGen *g, ImportTableEntry *import, BlockContext *context,
-//        AstNode *node)
-//{
-//    assert(node->type == NodeTypeFnCallExpr);
-//
-//    if (context->fn_entry) {
-//        add_node_error(g, node, buf_sprintf("@import invalid inside function bodies"));
-//        return g->builtin_types.entry_invalid;
-//    }
-//
-//    AstNode *first_param_node = node->data.fn_call_expr.params.at(0);
-//    Buf *import_target_str = resolve_const_expr_str(g, import, context, first_param_node->parent_field);
-//    if (!import_target_str) {
-//        return g->builtin_types.entry_invalid;
-//    }
-//
-//    Buf *import_target_path;
-//    Buf *search_dir;
-//    assert(import->package);
-//    PackageTableEntry *target_package;
-//    auto package_entry = import->package->package_table.maybe_get(import_target_str);
-//    if (package_entry) {
-//        target_package = package_entry->value;
-//        import_target_path = &target_package->root_src_path;
-//        search_dir = &target_package->root_src_dir;
-//    } else {
-//        // try it as a filename
-//        target_package = import->package;
-//        import_target_path = import_target_str;
-//        search_dir = &import->package->root_src_dir;
-//    }
-//
-//    Buf full_path = BUF_INIT;
-//    os_path_join(search_dir, import_target_path, &full_path);
-//
-//    Buf *import_code = buf_alloc();
-//    Buf *abs_full_path = buf_alloc();
-//    int err;
-//    if ((err = os_path_real(&full_path, abs_full_path))) {
-//        if (err == ErrorFileNotFound) {
-//            add_node_error(g, node,
-//                    buf_sprintf("unable to find '%s'", buf_ptr(import_target_path)));
-//            return g->builtin_types.entry_invalid;
-//        } else {
-//            g->error_during_imports = true;
-//            add_node_error(g, node,
-//                    buf_sprintf("unable to open '%s': %s", buf_ptr(&full_path), err_str(err)));
-//            return g->builtin_types.entry_invalid;
-//        }
-//    }
-//
-//    auto import_entry = g->import_table.maybe_get(abs_full_path);
-//    if (import_entry) {
-//        return resolve_expr_const_val_as_import(g, node, import_entry->value);
-//    }
-//
-//    if ((err = os_fetch_file_path(abs_full_path, import_code))) {
-//        if (err == ErrorFileNotFound) {
-//            add_node_error(g, node,
-//                    buf_sprintf("unable to find '%s'", buf_ptr(import_target_path)));
-//            return g->builtin_types.entry_invalid;
-//        } else {
-//            add_node_error(g, node,
-//                    buf_sprintf("unable to open '%s': %s", buf_ptr(&full_path), err_str(err)));
-//            return g->builtin_types.entry_invalid;
-//        }
-//    }
-//    ImportTableEntry *target_import = add_source_file(g, target_package,
-//            abs_full_path, search_dir, import_target_path, import_code);
-//
-//    scan_decls(g, target_import, target_import->block_context, target_import->root);
-//
-//    return resolve_expr_const_val_as_import(g, node, target_import);
-//}
-//
 //static TypeTableEntry *analyze_c_import(CodeGen *g, ImportTableEntry *parent_import,
 //        BlockContext *parent_context, AstNode *node)
 //{
@@ -6615,38 +6725,6 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
 //                buf_sprintf("type '%s' does not support field access", buf_ptr(&struct_type->name)));
 //            return g->builtin_types.entry_invalid;
 //        }
-//    } else if (struct_type->id == TypeTableEntryIdNamespace) {
-//        ConstExprValue *const_val = &get_resolved_expr(*struct_expr_node)->const_val;
-//        assert(const_val->ok);
-//        ImportTableEntry *namespace_import = const_val->data.x_import;
-//        AstNode *decl_node = find_decl(namespace_import->block_context, field_name);
-//        if (!decl_node) {
-//            // we must now resolve all the use decls
-//            for (size_t i = 0; i < namespace_import->use_decls.length; i += 1) {
-//                AstNode *use_decl_node = namespace_import->use_decls.at(i);
-//                if (!get_resolved_expr(use_decl_node->data.use.expr)->type_entry) {
-//                    preview_use_decl(g, use_decl_node);
-//                }
-//                resolve_use_decl(g, use_decl_node);
-//            }
-//            decl_node = find_decl(namespace_import->block_context, field_name);
-//        }
-//        if (decl_node) {
-//            TopLevelDecl *tld = get_as_top_level_decl(decl_node);
-//            if (tld->visib_mod == VisibModPrivate && decl_node->owner != import) {
-//                ErrorMsg *msg = add_node_error(g, node,
-//                    buf_sprintf("'%s' is private", buf_ptr(field_name)));
-//                add_error_note(g, msg, decl_node, buf_sprintf("declared here"));
-//            }
-//            bool pointer_only = false;
-//            return analyze_decl_ref(g, node, decl_node, pointer_only, context,
-//                    const_val->depends_on_compile_var);
-//        } else {
-//            const char *import_name = namespace_import->path ? buf_ptr(namespace_import->path) : "(C import)";
-//            add_node_error(g, node,
-//                buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), import_name));
-//            return g->builtin_types.entry_invalid;
-//        }
 //    } else {
 //        add_node_error(g, node,
 //            buf_sprintf("type '%s' does not support field access", buf_ptr(&struct_type->name)));
@@ -6654,490 +6732,6 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
 //    }
 //}
 //
-
-//static TypeTableEntry *analyze_lvalue(CodeGen *g, ImportTableEntry *import, BlockContext *block_context,
-//        AstNode *lhs_node, LValPurpose purpose, bool is_ptr_const)
-//{
-//    TypeTableEntry *expected_rhs_type = nullptr;
-//    lhs_node->block_context = block_context;
-//    if (lhs_node->type == NodeTypeSymbol) {
-//        bool pointer_only = purpose == LValPurposeAddressOf;
-//        expected_rhs_type = analyze_symbol_expr(g, import, block_context, nullptr, lhs_node, pointer_only);
-//        if (expected_rhs_type->id == TypeTableEntryIdInvalid) {
-//            return g->builtin_types.entry_invalid;
-//        }
-//        if (purpose != LValPurposeAddressOf) {
-//            Buf *name = lhs_node->data.symbol_expr.symbol;
-//            VariableTableEntry *var = find_variable(g, block_context, name);
-//            if (var) {
-//                if (var->src_is_const) {
-//                    add_node_error(g, lhs_node, buf_sprintf("cannot assign to constant"));
-//                    expected_rhs_type = g->builtin_types.entry_invalid;
-//                } else {
-//                    expected_rhs_type = var->type;
-//                    get_resolved_expr(lhs_node)->variable = var;
-//                }
-//            } else {
-//                add_node_error(g, lhs_node,
-//                        buf_sprintf("use of undeclared identifier '%s'", buf_ptr(name)));
-//                expected_rhs_type = g->builtin_types.entry_invalid;
-//            }
-//        }
-//    } else if (lhs_node->type == NodeTypeArrayAccessExpr) {
-//        expected_rhs_type = analyze_array_access_expr(g, import, block_context, lhs_node, purpose);
-//    } else if (lhs_node->type == NodeTypeFieldAccessExpr) {
-//        expected_rhs_type = analyze_field_access_expr(g, import, block_context, nullptr, lhs_node);
-//    } else if (lhs_node->type == NodeTypePrefixOpExpr &&
-//            lhs_node->data.prefix_op_expr.prefix_op == PrefixOpDereference)
-//    {
-//        assert(purpose == LValPurposeAssign);
-//        AstNode *target_node = lhs_node->data.prefix_op_expr.primary_expr;
-//        TypeTableEntry *type_entry = analyze_expression(g, import, block_context, nullptr, target_node);
-//        if (type_entry->id == TypeTableEntryIdInvalid) {
-//            expected_rhs_type = type_entry;
-//        } else if (type_entry->id == TypeTableEntryIdPointer) {
-//            expected_rhs_type = type_entry->data.pointer.child_type;
-//        } else {
-//            add_node_error(g, target_node,
-//                buf_sprintf("indirection requires pointer operand ('%s' invalid)",
-//                    buf_ptr(&type_entry->name)));
-//            expected_rhs_type = g->builtin_types.entry_invalid;
-//        }
-//    } else {
-//        if (purpose == LValPurposeAssign) {
-//            add_node_error(g, lhs_node, buf_sprintf("invalid assignment target"));
-//            expected_rhs_type = g->builtin_types.entry_invalid;
-//        } else if (purpose == LValPurposeAddressOf) {
-//            TypeTableEntry *type_entry = analyze_expression(g, import, block_context, nullptr, lhs_node);
-//            if (type_entry->id == TypeTableEntryIdInvalid) {
-//                expected_rhs_type = g->builtin_types.entry_invalid;
-//            } else if (type_entry->id == TypeTableEntryIdMetaType) {
-//                expected_rhs_type = type_entry;
-//            } else {
-//                add_node_error(g, lhs_node, buf_sprintf("invalid addressof target"));
-//                expected_rhs_type = g->builtin_types.entry_invalid;
-//            }
-//        }
-//    }
-//    assert(expected_rhs_type);
-//    return expected_rhs_type;
-//}
-
-
-
-//static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
-//        TypeTableEntry *expected_type, AstNode *node)
-//{
-//    assert(node->type == NodeTypeBinOpExpr);
-//    BinOpType bin_op_type = node->data.bin_op_expr.bin_op;
-//    switch (bin_op_type) {
-//        case BinOpTypeAssign:
-//        case BinOpTypeAssignTimes:
-//        case BinOpTypeAssignTimesWrap:
-//        case BinOpTypeAssignDiv:
-//        case BinOpTypeAssignMod:
-//        case BinOpTypeAssignPlus:
-//        case BinOpTypeAssignPlusWrap:
-//        case BinOpTypeAssignMinus:
-//        case BinOpTypeAssignMinusWrap:
-//        case BinOpTypeAssignBitShiftLeft:
-//        case BinOpTypeAssignBitShiftLeftWrap:
-//        case BinOpTypeAssignBitShiftRight:
-//        case BinOpTypeAssignBitAnd:
-//        case BinOpTypeAssignBitXor:
-//        case BinOpTypeAssignBitOr:
-//        case BinOpTypeAssignBoolAnd:
-//        case BinOpTypeAssignBoolOr:
-//            {
-//                AstNode *lhs_node = node->data.bin_op_expr.op1;
-//
-//                TypeTableEntry *expected_rhs_type = analyze_lvalue(g, import, context, lhs_node,
-//                        LValPurposeAssign, false);
-//                if (expected_rhs_type->id == TypeTableEntryIdInvalid) {
-//                    return g->builtin_types.entry_invalid;
-//                } else if (!is_op_allowed(expected_rhs_type, node->data.bin_op_expr.bin_op)) {
-//                    if (expected_rhs_type->id != TypeTableEntryIdInvalid) {
-//                        add_node_error(g, lhs_node,
-//                            buf_sprintf("operator not allowed for type '%s'",
-//                                buf_ptr(&expected_rhs_type->name)));
-//                    }
-//                }
-//
-//                analyze_expression(g, import, context, expected_rhs_type, node->data.bin_op_expr.op2);
-//                // not const ok because expression has side effects
-//                return g->builtin_types.entry_void;
-//            }
-//        case BinOpTypeBoolOr:
-//        case BinOpTypeBoolAnd:
-//            return analyze_logic_bin_op_expr(g, import, context, node);
-//        case BinOpTypeCmpEq:
-//        case BinOpTypeCmpNotEq:
-//        case BinOpTypeCmpLessThan:
-//        case BinOpTypeCmpGreaterThan:
-//        case BinOpTypeCmpLessOrEq:
-//        case BinOpTypeCmpGreaterOrEq:
-//            return analyze_bool_bin_op_expr(g, import, context, node);
-//        case BinOpTypeBinOr:
-//        case BinOpTypeBinXor:
-//        case BinOpTypeBinAnd:
-//        case BinOpTypeBitShiftLeft:
-//        case BinOpTypeBitShiftLeftWrap:
-//        case BinOpTypeBitShiftRight:
-//        case BinOpTypeAdd:
-//        case BinOpTypeAddWrap:
-//        case BinOpTypeSub:
-//        case BinOpTypeSubWrap:
-//        case BinOpTypeMult:
-//        case BinOpTypeMultWrap:
-//        case BinOpTypeDiv:
-//        case BinOpTypeMod:
-//            {
-//                AstNode **op1 = node->data.bin_op_expr.op1->parent_field;
-//                AstNode **op2 = node->data.bin_op_expr.op2->parent_field;
-//                TypeTableEntry *lhs_type = analyze_expression(g, import, context, nullptr, *op1);
-//                TypeTableEntry *rhs_type = analyze_expression(g, import, context, nullptr, *op2);
-//
-//                AstNode *op_nodes[] = {*op1, *op2};
-//                TypeTableEntry *op_types[] = {lhs_type, rhs_type};
-//
-//                TypeTableEntry *resolved_type = resolve_peer_type_compatibility(g, import, context, node,
-//                        op_nodes, op_types, 2);
-//
-//                if (resolved_type->id == TypeTableEntryIdInvalid) {
-//                    return resolved_type;
-//                }
-//
-//                if (resolved_type->id == TypeTableEntryIdInt ||
-//                    resolved_type->id == TypeTableEntryIdNumLitInt)
-//                {
-//                    // int
-//                } else if ((resolved_type->id == TypeTableEntryIdFloat ||
-//                           resolved_type->id == TypeTableEntryIdNumLitFloat) &&
-//                    (bin_op_type == BinOpTypeAdd ||
-//                     bin_op_type == BinOpTypeSub ||
-//                     bin_op_type == BinOpTypeMult ||
-//                     bin_op_type == BinOpTypeDiv ||
-//                     bin_op_type == BinOpTypeMod))
-//                {
-//                    // float
-//                } else {
-//                    add_node_error(g, node, buf_sprintf("invalid operands to binary expression: '%s' and '%s'",
-//                            buf_ptr(&lhs_type->name), buf_ptr(&rhs_type->name)));
-//                    return g->builtin_types.entry_invalid;
-//                }
-//
-//                ConstExprValue *op1_val = &get_resolved_expr(*op1)->const_val;
-//                ConstExprValue *op2_val = &get_resolved_expr(*op2)->const_val;
-//                if (!op1_val->ok || !op2_val->ok) {
-//                    return resolved_type;
-//                }
-//
-//                ConstExprValue *out_val = &get_resolved_expr(node)->const_val;
-//                int err;
-//                if ((err = eval_const_expr_bin_op(op1_val, resolved_type, bin_op_type,
-//                                op2_val, resolved_type, out_val)))
-//                {
-//                    if (err == ErrorDivByZero) {
-//                        add_node_error(g, node, buf_sprintf("division by zero is undefined"));
-//                        return g->builtin_types.entry_invalid;
-//                    } else if (err == ErrorOverflow) {
-//                        add_node_error(g, node, buf_sprintf("value cannot be represented in any integer type"));
-//                        return g->builtin_types.entry_invalid;
-//                    }
-//                    return g->builtin_types.entry_invalid;
-//                }
-//
-//                num_lit_fits_in_other_type(g, node, resolved_type);
-//                return resolved_type;
-//            }
-//        case BinOpTypeUnwrapMaybe:
-//            {
-//                AstNode *op1 = node->data.bin_op_expr.op1;
-//                AstNode *op2 = node->data.bin_op_expr.op2;
-//                TypeTableEntry *lhs_type = analyze_expression(g, import, context, nullptr, op1);
-//
-//                if (lhs_type->id == TypeTableEntryIdInvalid) {
-//                    return lhs_type;
-//                } else if (lhs_type->id == TypeTableEntryIdMaybe) {
-//                    TypeTableEntry *child_type = lhs_type->data.maybe.child_type;
-//                    analyze_expression(g, import, context, child_type, op2);
-//                    return child_type;
-//                } else {
-//                    add_node_error(g, op1,
-//                        buf_sprintf("expected maybe type, found '%s'",
-//                            buf_ptr(&lhs_type->name)));
-//                    return g->builtin_types.entry_invalid;
-//                }
-//            }
-//        case BinOpTypeArrayCat:
-//            {
-//                AstNode **op1 = node->data.bin_op_expr.op1->parent_field;
-//                AstNode **op2 = node->data.bin_op_expr.op2->parent_field;
-//
-//                TypeTableEntry *op1_type = analyze_expression(g, import, context, nullptr, *op1);
-//                TypeTableEntry *child_type;
-//                if (op1_type->id == TypeTableEntryIdInvalid) {
-//                    return g->builtin_types.entry_invalid;
-//                } else if (op1_type->id == TypeTableEntryIdArray) {
-//                    child_type = op1_type->data.array.child_type;
-//                } else if (op1_type->id == TypeTableEntryIdPointer &&
-//                           op1_type->data.pointer.child_type == g->builtin_types.entry_u8) {
-//                    child_type = op1_type->data.pointer.child_type;
-//                } else {
-//                    add_node_error(g, *op1, buf_sprintf("expected array or C string literal, found '%s'",
-//                                buf_ptr(&op1_type->name)));
-//                    return g->builtin_types.entry_invalid;
-//                }
-//
-//                TypeTableEntry *op2_type = analyze_expression(g, import, context, nullptr, *op2);
-//
-//                if (op2_type->id == TypeTableEntryIdInvalid) {
-//                    return g->builtin_types.entry_invalid;
-//                } else if (op2_type->id == TypeTableEntryIdArray) {
-//                    if (op2_type->data.array.child_type != child_type) {
-//                        add_node_error(g, *op2, buf_sprintf("expected array of type '%s', found '%s'",
-//                                    buf_ptr(&child_type->name),
-//                                    buf_ptr(&op2_type->name)));
-//                        return g->builtin_types.entry_invalid;
-//                    }
-//                } else if (op2_type->id == TypeTableEntryIdPointer &&
-//                        op2_type->data.pointer.child_type == g->builtin_types.entry_u8) {
-//                } else {
-//                    add_node_error(g, *op2, buf_sprintf("expected array or C string literal, found '%s'",
-//                                buf_ptr(&op2_type->name)));
-//                    return g->builtin_types.entry_invalid;
-//                }
-//
-//                ConstExprValue *op1_val = &get_resolved_expr(*op1)->const_val;
-//                ConstExprValue *op2_val = &get_resolved_expr(*op2)->const_val;
-//
-//                AstNode *bad_node;
-//                if (!op1_val->ok) {
-//                    bad_node = *op1;
-//                } else if (!op2_val->ok) {
-//                    bad_node = *op2;
-//                } else {
-//                    bad_node = nullptr;
-//                }
-//                if (bad_node) {
-//                    add_node_error(g, bad_node, buf_sprintf("array concatenation requires constant expression"));
-//                    return g->builtin_types.entry_invalid;
-//                }
-//
-//                ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
-//                const_val->ok = true;
-//                const_val->depends_on_compile_var = op1_val->depends_on_compile_var ||
-//                    op2_val->depends_on_compile_var;
-//
-//                if (op1_type->id == TypeTableEntryIdArray) {
-//                    uint64_t new_len = op1_type->data.array.len + op2_type->data.array.len;
-//                    const_val->data.x_array.fields = allocate<ConstExprValue*>(new_len);
-//                    uint64_t next_index = 0;
-//                    for (uint64_t i = 0; i < op1_type->data.array.len; i += 1, next_index += 1) {
-//                        const_val->data.x_array.fields[next_index] = op1_val->data.x_array.fields[i];
-//                    }
-//                    for (uint64_t i = 0; i < op2_type->data.array.len; i += 1, next_index += 1) {
-//                        const_val->data.x_array.fields[next_index] = op2_val->data.x_array.fields[i];
-//                    }
-//                    return get_array_type(g, child_type, new_len);
-//                } else if (op1_type->id == TypeTableEntryIdPointer) {
-//                    if (!op1_val->data.x_ptr.is_c_str) {
-//                        add_node_error(g, *op1,
-//                                buf_sprintf("expected array or C string literal, found '%s'",
-//                                    buf_ptr(&op1_type->name)));
-//                        return g->builtin_types.entry_invalid;
-//                    } else if (!op2_val->data.x_ptr.is_c_str) {
-//                        add_node_error(g, *op2,
-//                                buf_sprintf("expected array or C string literal, found '%s'",
-//                                    buf_ptr(&op2_type->name)));
-//                        return g->builtin_types.entry_invalid;
-//                    }
-//                    const_val->data.x_ptr.is_c_str = true;
-//                    const_val->data.x_ptr.len = op1_val->data.x_ptr.len + op2_val->data.x_ptr.len - 1;
-//                    const_val->data.x_ptr.ptr = allocate<ConstExprValue*>(const_val->data.x_ptr.len);
-//                    uint64_t next_index = 0;
-//                    for (uint64_t i = 0; i < op1_val->data.x_ptr.len - 1; i += 1, next_index += 1) {
-//                        const_val->data.x_ptr.ptr[next_index] = op1_val->data.x_ptr.ptr[i];
-//                    }
-//                    for (uint64_t i = 0; i < op2_val->data.x_ptr.len; i += 1, next_index += 1) {
-//                        const_val->data.x_ptr.ptr[next_index] = op2_val->data.x_ptr.ptr[i];
-//                    }
-//                    return op1_type;
-//                } else {
-//                    zig_unreachable();
-//                }
-//            }
-//        case BinOpTypeArrayMult:
-//            return analyze_array_mult(g, import, context, expected_type, node);
-//        case BinOpTypeInvalid:
-//            zig_unreachable();
-//    }
-//    zig_unreachable();
-//}
-
-
-//static TypeTableEntry *analyze_bool_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
-//        AstNode *node)
-//{
-//    assert(node->type == NodeTypeBinOpExpr);
-//    BinOpType bin_op_type = node->data.bin_op_expr.bin_op;
-//
-//    AstNode **op1 = &node->data.bin_op_expr.op1;
-//    AstNode **op2 = &node->data.bin_op_expr.op2;
-//    TypeTableEntry *op1_type = analyze_expression(g, import, context, nullptr, *op1);
-//    TypeTableEntry *op2_type = analyze_expression(g, import, context, nullptr, *op2);
-//
-//    AstNode *op_nodes[] = {*op1, *op2};
-//    TypeTableEntry *op_types[] = {op1_type, op2_type};
-//
-//    TypeTableEntry *resolved_type = resolve_peer_type_compatibility(g, import, context, node,
-//            op_nodes, op_types, 2);
-//
-//    bool is_equality_cmp = (bin_op_type == BinOpTypeCmpEq || bin_op_type == BinOpTypeCmpNotEq);
-//
-//    switch (resolved_type->id) {
-//        case TypeTableEntryIdInvalid:
-//            return g->builtin_types.entry_invalid;
-//
-//        case TypeTableEntryIdNumLitFloat:
-//        case TypeTableEntryIdNumLitInt:
-//        case TypeTableEntryIdInt:
-//        case TypeTableEntryIdFloat:
-//            break;
-//
-//        case TypeTableEntryIdBool:
-//        case TypeTableEntryIdMetaType:
-//        case TypeTableEntryIdVoid:
-//        case TypeTableEntryIdPointer:
-//        case TypeTableEntryIdPureError:
-//        case TypeTableEntryIdFn:
-//        case TypeTableEntryIdTypeDecl:
-//        case TypeTableEntryIdNamespace:
-//        case TypeTableEntryIdBlock:
-//        case TypeTableEntryIdGenericFn:
-//            if (!is_equality_cmp) {
-//                add_node_error(g, node,
-//                    buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name)));
-//                return g->builtin_types.entry_invalid;
-//            }
-//            break;
-//
-//        case TypeTableEntryIdEnum:
-//            if (!is_equality_cmp || resolved_type->data.enumeration.gen_field_count != 0) {
-//                add_node_error(g, node,
-//                    buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name)));
-//                return g->builtin_types.entry_invalid;
-//            }
-//            break;
-//
-//        case TypeTableEntryIdUnreachable:
-//        case TypeTableEntryIdArray:
-//        case TypeTableEntryIdStruct:
-//        case TypeTableEntryIdUndefLit:
-//        case TypeTableEntryIdNullLit:
-//        case TypeTableEntryIdMaybe:
-//        case TypeTableEntryIdErrorUnion:
-//        case TypeTableEntryIdUnion:
-//            add_node_error(g, node,
-//                buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name)));
-//            return g->builtin_types.entry_invalid;
-//
-//        case TypeTableEntryIdVar:
-//            zig_unreachable();
-//    }
-//
-//    ConstExprValue *op1_val = &get_resolved_expr(*op1)->const_val;
-//    ConstExprValue *op2_val = &get_resolved_expr(*op2)->const_val;
-//    if (!op1_val->ok || !op2_val->ok) {
-//        return g->builtin_types.entry_bool;
-//    }
-//
-//
-//    ConstExprValue *out_val = &get_resolved_expr(node)->const_val;
-//    eval_const_expr_bin_op(op1_val, op1_type, bin_op_type, op2_val, op2_type, out_val);
-//    return g->builtin_types.entry_bool;
-//
-//}
-//
-////
-//static TypeTableEntry *analyze_if(CodeGen *g, ImportTableEntry *import, BlockContext *parent_context,
-//        TypeTableEntry *expected_type, AstNode *node,
-//        AstNode **then_node, AstNode **else_node, bool cond_is_const, bool cond_bool_val)
-//{
-//    if (!*else_node) {
-//        *else_node = create_ast_void_node(g, import, node);
-//        normalize_parent_ptrs(node);
-//    }
-//
-//    BlockContext *then_context;
-//    BlockContext *else_context;
-//    if (cond_is_const) {
-//        if (cond_bool_val) {
-//            then_context = parent_context;
-//            else_context = new_block_context(node, parent_context);
-//
-//            else_context->codegen_excluded = true;
-//        } else {
-//            then_context = new_block_context(node, parent_context);
-//            else_context = parent_context;
-//
-//            then_context->codegen_excluded = true;
-//        }
-//    } else {
-//        then_context = parent_context;
-//        else_context = parent_context;
-//    }
-//
-//    TypeTableEntry *then_type = nullptr;
-//    TypeTableEntry *else_type = nullptr;
-//
-//    if (!then_context->codegen_excluded) {
-//        then_type = analyze_expression(g, import, then_context, expected_type, *then_node);
-//        if (then_type->id == TypeTableEntryIdInvalid) {
-//            return g->builtin_types.entry_invalid;
-//        }
-//    }
-//    if (!else_context->codegen_excluded) {
-//        else_type = analyze_expression(g, import, else_context, expected_type, *else_node);
-//        if (else_type->id == TypeTableEntryIdInvalid) {
-//            return g->builtin_types.entry_invalid;
-//        }
-//    }
-//
-//    TypeTableEntry *result_type;
-//    if (then_context->codegen_excluded) {
-//        result_type = else_type;
-//    } else if (else_context->codegen_excluded) {
-//        result_type = then_type;
-//    } else if (expected_type) {
-//        result_type = (then_type->id == TypeTableEntryIdUnreachable) ? else_type : then_type;
-//    } else {
-//        AstNode *op_nodes[] = {*then_node, *else_node};
-//        TypeTableEntry *op_types[] = {then_type, else_type};
-//        result_type = resolve_peer_type_compatibility(g, import, parent_context, node, op_nodes, op_types, 2);
-//    }
-//
-//    if (!cond_is_const) {
-//        return add_error_if_type_is_num_lit(g, result_type, node);
-//    }
-//
-//    ConstExprValue *other_const_val;
-//    if (cond_bool_val) {
-//        other_const_val = &get_resolved_expr(*then_node)->const_val;
-//    } else {
-//        other_const_val = &get_resolved_expr(*else_node)->const_val;
-//    }
-//    if (!other_const_val->ok) {
-//        return add_error_if_type_is_num_lit(g, result_type, node);
-//    }
-//
-//    ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
-//    *const_val = *other_const_val;
-//    // the condition depends on a compile var, so the entire if statement does too
-//    const_val->depends_on_compile_var = true;
-//    return result_type;
-//}
-//
 //static TypeTableEntry *bad_method_call(CodeGen *g, AstNode *node, TypeTableEntry *container_type,
 //        TypeTableEntry *expected_param_type, FnTableEntry *fn_table_entry)
 //{
@@ -8195,92 +7789,6 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
 //    return g->builtin_types.entry_invalid;
 //}
 //
-//static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
-//        TypeTableEntry *expected_type, AstNode *node, bool pointer_only)
-//{
-//    Buf *variable_name = node->data.symbol_expr.symbol;
-//
-//    auto primitive_table_entry = g->primitive_type_table.maybe_get(variable_name);
-//    if (primitive_table_entry) {
-//        return resolve_expr_const_val_as_type(g, node, primitive_table_entry->value, false);
-//    }
-//
-//    VariableTableEntry *var = find_variable(g, context, variable_name);
-//    if (var) {
-//        TypeTableEntry *var_type = analyze_var_ref(g, node, var, context, false);
-//        return var_type;
-//    }
-//
-//    AstNode *decl_node = find_decl(context, variable_name);
-//    if (decl_node) {
-//        return analyze_decl_ref(g, node, decl_node, pointer_only, context, false);
-//    }
-//
-//    if (import->any_imports_failed) {
-//        // skip the error message since we had a failing import in this file
-//        // if an import breaks we don't need 9999 undeclared identifier errors
-//        return g->builtin_types.entry_invalid;
-//    }
-//
-//    mark_impure_fn(g, context, node);
-//    add_node_error(g, node, buf_sprintf("use of undeclared identifier '%s'", buf_ptr(variable_name)));
-//    return g->builtin_types.entry_invalid;
-//}
-//
-//static TypeTableEntry *analyze_decl_ref(CodeGen *g, AstNode *source_node, AstNode *decl_node,
-//        bool pointer_only, BlockContext *block_context, bool depends_on_compile_var)
-//{
-//    resolve_top_level_decl(g, decl_node, pointer_only);
-//    TopLevelDecl *tld = get_as_top_level_decl(decl_node);
-//    if (tld->resolution == TldResolutionInvalid) {
-//        return g->builtin_types.entry_invalid;
-//    }
-//
-//    if (decl_node->type == NodeTypeVariableDeclaration) {
-//        VariableTableEntry *var = decl_node->data.variable_declaration.variable;
-//        return analyze_var_ref(g, source_node, var, block_context, depends_on_compile_var);
-//    } else if (decl_node->type == NodeTypeFnProto) {
-//        FnTableEntry *fn_entry = decl_node->data.fn_proto.fn_table_entry;
-//        assert(fn_entry->type_entry);
-//        if (fn_entry->type_entry->id == TypeTableEntryIdGenericFn) {
-//            return resolve_expr_const_val_as_generic_fn(g, source_node, fn_entry->type_entry, depends_on_compile_var);
-//        } else {
-//            return resolve_expr_const_val_as_fn(g, source_node, fn_entry, depends_on_compile_var);
-//        }
-//    } else if (decl_node->type == NodeTypeContainerDecl) {
-//        if (decl_node->data.struct_decl.generic_params.length > 0) {
-//            TypeTableEntry *type_entry = decl_node->data.struct_decl.generic_fn_type;
-//            assert(type_entry);
-//            return resolve_expr_const_val_as_generic_fn(g, source_node, type_entry, depends_on_compile_var);
-//        } else {
-//            return resolve_expr_const_val_as_type(g, source_node, decl_node->data.struct_decl.type_entry,
-//                    depends_on_compile_var);
-//        }
-//    } else if (decl_node->type == NodeTypeTypeDecl) {
-//        return resolve_expr_const_val_as_type(g, source_node, decl_node->data.type_decl.child_type_entry,
-//                depends_on_compile_var);
-//    } else {
-//        zig_unreachable();
-//    }
-//}
-//
-//static TypeTableEntry *analyze_var_ref(CodeGen *g, AstNode *source_node, VariableTableEntry *var,
-//        BlockContext *context, bool depends_on_compile_var)
-//{
-//    get_resolved_expr(source_node)->variable = var;
-//    if (!var_is_pure(var, context)) {
-//        mark_impure_fn(g, context, source_node);
-//    }
-//    if (var->src_is_const && var->val_node) {
-//        ConstExprValue *other_const_val = &get_resolved_expr(var->val_node)->const_val;
-//        if (other_const_val->ok) {
-//            return resolve_expr_const_val_as_other_expr(g, source_node, var->val_node,
-//                    depends_on_compile_var || var->force_depends_on_compile_var);
-//        }
-//    }
-//    return var->type;
-//}
-//
 //static TypeTableEntry *analyze_fn_proto_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
 //        TypeTableEntry *expected_type, AstNode *node)
 //{
@@ -8294,14 +7802,6 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
 //    return resolve_expr_const_val_as_type(g, node, type_entry, false);
 //}
 //
-//static bool var_is_pure(VariableTableEntry *var, BlockContext *context) {
-//    if (var->block_context->fn_entry == context->fn_entry) {
-//        // variable was declared in the current function, so it's OK.
-//        return true;
-//    }
-//    return var->src_is_const && var->type->deep_const;
-//}
-//
 //static void validate_voided_expr(CodeGen *g, AstNode *source_node, TypeTableEntry *type_entry) {
 //    if (type_entry->id == TypeTableEntryIdMetaType) {
 //        add_node_error(g, first_executing_node(source_node), buf_sprintf("expected expression, found type"));
@@ -8949,54 +8449,6 @@ static void analyze_goto_pass2(CodeGen *g, ImportTableEntry *import, AstNode *no
 //}
 //
 //
-//static LLVMValueRef gen_lvalue(CodeGen *g, AstNode *expr_node, AstNode *node,
-//        TypeTableEntry **out_type_entry)
-//{
-//    LLVMValueRef target_ref;
-//
-//    if (node->type == NodeTypeSymbol) {
-//        VariableTableEntry *var = get_resolved_expr(node)->variable;
-//        assert(var);
-//
-//        *out_type_entry = var->type;
-//        target_ref = var->value_ref;
-//    } else if (node->type == NodeTypeArrayAccessExpr) {
-//        TypeTableEntry *array_type = get_expr_type(node->data.array_access_expr.array_ref_expr);
-//        if (array_type->id == TypeTableEntryIdArray) {
-//            *out_type_entry = array_type->data.array.child_type;
-//            target_ref = gen_array_ptr(g, node);
-//        } else if (array_type->id == TypeTableEntryIdPointer) {
-//            *out_type_entry = array_type->data.pointer.child_type;
-//            target_ref = gen_array_ptr(g, node);
-//        } else if (array_type->id == TypeTableEntryIdStruct) {
-//            assert(array_type->data.structure.is_slice);
-//            *out_type_entry = array_type->data.structure.fields[0].type_entry->data.pointer.child_type;
-//            target_ref = gen_array_ptr(g, node);
-//        } else {
-//            zig_unreachable();
-//        }
-//    } else if (node->type == NodeTypeFieldAccessExpr) {
-//        AstNode *struct_expr_node = node->data.field_access_expr.struct_expr;
-//        TypeTableEntry *struct_type = get_expr_type(struct_expr_node);
-//        if (struct_type->id == TypeTableEntryIdNamespace) {
-//            target_ref = gen_field_access_expr(g, node, true);
-//            *out_type_entry = get_expr_type(node);
-//        } else {
-//            target_ref = gen_field_ptr(g, node, out_type_entry);
-//        }
-//    } else if (node->type == NodeTypePrefixOpExpr) {
-//        assert(node->data.prefix_op_expr.prefix_op == PrefixOpDereference);
-//        AstNode *target_expr = node->data.prefix_op_expr.primary_expr;
-//        TypeTableEntry *type_entry = get_expr_type(target_expr);
-//        assert(type_entry->id == TypeTableEntryIdPointer);
-//        *out_type_entry = type_entry->data.pointer.child_type;
-//        return gen_expr(g, target_expr);
-//    } else {
-//        zig_panic("bad assign target");
-//    }
-//
-//    return target_ref;
-//}
 //
 //static LLVMValueRef gen_bool_and_expr(CodeGen *g, AstNode *node) {
 //    assert(node->type == NodeTypeBinOpExpr);
src/ir_print.cpp
@@ -115,6 +115,12 @@ static void ir_print_const_value(IrPrint *irp, TypeTableEntry *type_entry, Const
                 }
                 break;
             }
+        case TypeTableEntryIdNamespace:
+            {
+                ImportTableEntry *import = const_val->data.x_import;
+                fprintf(irp->f, "(namespace: %s)", buf_ptr(import->path));
+                break;
+            }
         case TypeTableEntryIdVar:
         case TypeTableEntryIdFloat:
         case TypeTableEntryIdStruct:
@@ -124,7 +130,6 @@ static void ir_print_const_value(IrPrint *irp, TypeTableEntry *type_entry, Const
         case TypeTableEntryIdEnum:
         case TypeTableEntryIdUnion:
         case TypeTableEntryIdTypeDecl:
-        case TypeTableEntryIdNamespace:
         case TypeTableEntryIdGenericFn:
             zig_panic("TODO render more constant types in IR printer");
     }
@@ -407,11 +412,6 @@ static void ir_print_field_ptr(IrPrint *irp, IrInstructionFieldPtr *instruction)
     fprintf(irp->f, ")");
 }
 
-static void ir_print_read_field(IrPrint *irp, IrInstructionReadField *instruction) {
-    ir_print_other_instruction(irp, instruction->container_ptr);
-    fprintf(irp->f, ".%s", buf_ptr(instruction->field_name));
-}
-
 static void ir_print_struct_field_ptr(IrPrint *irp, IrInstructionStructFieldPtr *instruction) {
     fprintf(irp->f, "@StructFieldPtr(&");
     ir_print_other_instruction(irp, instruction->struct_ptr);
@@ -575,6 +575,17 @@ static void ir_print_static_eval(IrPrint *irp, IrInstructionStaticEval *instruct
     fprintf(irp->f, ")");
 }
 
+static void ir_print_import(IrPrint *irp, IrInstructionImport *instruction) {
+    fprintf(irp->f, "@import(");
+    ir_print_other_instruction(irp, instruction->name);
+    fprintf(irp->f, ")");
+}
+
+static void ir_print_array_len(IrPrint *irp, IrInstructionArrayLen *instruction) {
+    ir_print_other_instruction(irp, instruction->array_value);
+    fprintf(irp->f, ".len");
+}
+
 static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
     ir_print_prefix(irp, instruction);
     switch (instruction->id) {
@@ -643,9 +654,6 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
         case IrInstructionIdFieldPtr:
             ir_print_field_ptr(irp, (IrInstructionFieldPtr *)instruction);
             break;
-        case IrInstructionIdReadField:
-            ir_print_read_field(irp, (IrInstructionReadField *)instruction);
-            break;
         case IrInstructionIdStructFieldPtr:
             ir_print_struct_field_ptr(irp, (IrInstructionStructFieldPtr *)instruction);
             break;
@@ -700,6 +708,12 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
         case IrInstructionIdStaticEval:
             ir_print_static_eval(irp, (IrInstructionStaticEval *)instruction);
             break;
+        case IrInstructionIdImport:
+            ir_print_import(irp, (IrInstructionImport *)instruction);
+            break;
+        case IrInstructionIdArrayLen:
+            ir_print_array_len(irp, (IrInstructionArrayLen *)instruction);
+            break;
     }
     fprintf(irp->f, "\n");
 }
test/cases/namespace_fn_call.zig
@@ -0,0 +1,1 @@
+pub fn foo() -> i32 { 1234 }
test/self_hosted2.zig
@@ -1,3 +1,5 @@
+const case_namespace_fn_call = @import("cases/namespace_fn_call.zig");
+
 pub const SYS_write = 1;
 pub const SYS_exit = 60;
 pub const stdout_fileno = 1;
@@ -61,6 +63,10 @@ fn testInlineSwitch() {
     assert(result + 1 == 14);
 }
 
+fn testNamespaceFnCall() {
+    assert(case_namespace_fn_call.foo() == 1234);
+}
+
 fn assert(ok: bool) {
     if (!ok)
         @unreachable();
@@ -73,6 +79,7 @@ fn runAllTests() {
     switchWithNumbers();
     switchWithAllRanges();
     testInlineSwitch();
+    testNamespaceFnCall();
 }
 
 export nakedcc fn _start() -> unreachable {