Commit eb5693d91f

Andrew Kelley <superjoe30@gmail.com>
2016-11-28 08:40:01
IR: function call porting progress
also implemented container init generics is still todo
1 parent 9e7c475
src/all_types.hpp
@@ -41,6 +41,7 @@ struct IrExecutable {
     bool invalid;
     ZigList<LabelTableEntry *> all_labels;
     ZigList<AstNode *> goto_list;
+    bool is_inline;
 };
 
 enum OutType {
@@ -82,6 +83,11 @@ struct ConstErrValue {
     ConstExprValue *payload;
 };
 
+struct ConstBoundFnValue {
+    FnTableEntry *fn;
+    IrInstruction *first_arg;
+};
+
 enum ConstValSpecial {
     ConstValSpecialRuntime,
     ConstValSpecialStatic,
@@ -100,6 +106,7 @@ struct ConstExprValue {
         BigNum x_bignum;
         bool x_bool;
         FnTableEntry *x_fn;
+        ConstBoundFnValue x_bound_fn;
         TypeTableEntry *x_type;
         ConstExprValue *x_maybe;
         ConstErrValue x_err;
@@ -494,6 +501,7 @@ struct AstNodeIfBoolExpr {
     AstNode *condition;
     AstNode *then_block;
     AstNode *else_node; // null, block node, or other if expr node
+    bool is_inline; // TODO
 
     // populated by semantic analyzer
 };
@@ -503,6 +511,7 @@ struct AstNodeIfVarExpr {
     AstNode *then_block;
     AstNode *else_node; // null, block node, or other if expr node
     bool var_is_ptr;
+    bool is_inline; // TODO
 
     // populated by semantic analyzer
     TypeTableEntry *type;
@@ -941,12 +950,18 @@ struct TypeTableEntryFn {
 
     LLVMTypeRef raw_type_ref;
     LLVMCallConv calling_convention;
+
+    TypeTableEntry *bound_fn_parent;
 };
 
 struct TypeTableEntryGenericFn {
     AstNode *decl_node;
 };
 
+struct TypeTableEntryBoundFn {
+    TypeTableEntry *fn_type;
+};
+
 struct TypeTableEntryTypeDecl {
     TypeTableEntry *child_type;
     TypeTableEntry *canonical_type;
@@ -978,6 +993,7 @@ enum TypeTableEntryId {
     TypeTableEntryIdNamespace,
     TypeTableEntryIdBlock,
     TypeTableEntryIdGenericFn,
+    TypeTableEntryIdBoundFn,
 };
 
 struct TypeTableEntry {
@@ -988,7 +1004,6 @@ struct TypeTableEntry {
     ZigLLVMDIType *di_type;
 
     bool zero_bits;
-    bool deep_const;
 
     union {
         TypeTableEntryPointer pointer;
@@ -1003,6 +1018,7 @@ struct TypeTableEntry {
         TypeTableEntryFn fn;
         TypeTableEntryTypeDecl type_decl;
         TypeTableEntryGenericFn generic_fn;
+        TypeTableEntryBoundFn bound_fn;
     } data;
 
     // use these fields to make sure we don't duplicate type table entries for the same type
@@ -1043,12 +1059,6 @@ enum FnAnalState {
 };
 
 
-enum WantPure {
-    WantPureAuto,
-    WantPureFalse,
-    WantPureTrue,
-};
-
 enum FnInline {
     FnInlineAuto,
     FnInlineAlways,
@@ -1067,10 +1077,6 @@ struct FnTableEntry {
     bool internal_linkage;
     bool is_extern;
     bool is_test;
-    bool is_pure;
-    WantPure want_pure;
-    AstNode *want_pure_attr_node;
-    AstNode *want_pure_return_type;
     FnInline fn_inline;
     FnAnalState anal_state;
     IrExecutable ir_executable;
@@ -1085,6 +1091,9 @@ struct FnTableEntry {
     ZigList<VariableTableEntry *> variable_list;
 };
 
+uint32_t fn_table_entry_hash(FnTableEntry*);
+bool fn_table_entry_eql(FnTableEntry *a, FnTableEntry *b);
+
 enum BuiltinFnId {
     BuiltinFnIdInvalid,
     BuiltinFnIdMemcpy,
@@ -1122,7 +1131,6 @@ enum BuiltinFnId {
     BuiltinFnIdUnreachable,
     BuiltinFnIdSetFnTest,
     BuiltinFnIdSetFnVisible,
-    BuiltinFnIdSetFnStaticEval,
     BuiltinFnIdSetFnNoInline,
     BuiltinFnIdSetDebugSafety,
 };
@@ -1385,6 +1393,7 @@ enum IrInstructionId {
     IrInstructionIdStorePtr,
     IrInstructionIdFieldPtr,
     IrInstructionIdStructFieldPtr,
+    IrInstructionIdEnumFieldPtr,
     IrInstructionIdElemPtr,
     IrInstructionIdVarPtr,
     IrInstructionIdCall,
@@ -1393,6 +1402,7 @@ enum IrInstructionId {
     IrInstructionIdCast,
     IrInstructionIdContainerInitList,
     IrInstructionIdContainerInitFields,
+    IrInstructionIdStructInit,
     IrInstructionIdUnreachable,
     IrInstructionIdTypeOf,
     IrInstructionIdToPtrType,
@@ -1578,6 +1588,14 @@ struct IrInstructionStructFieldPtr {
     bool is_const;
 };
 
+struct IrInstructionEnumFieldPtr {
+    IrInstruction base;
+
+    IrInstruction *enum_ptr;
+    TypeEnumField *field;
+    bool is_const;
+};
+
 struct IrInstructionElemPtr {
     IrInstruction base;
 
@@ -1597,9 +1615,12 @@ struct IrInstructionVarPtr {
 struct IrInstructionCall {
     IrInstruction base;
 
-    IrInstruction *fn;
+    IrInstruction *fn_ref;
+    FnTableEntry *fn_entry;
     size_t arg_count;
     IrInstruction **args;
+    bool is_inline;
+    LLVMValueRef tmp_ptr;
 };
 
 struct IrInstructionConst {
@@ -1615,11 +1636,12 @@ struct IrInstructionReturn {
     IrInstruction *value;
 };
 
+// TODO get rid of this instruction, replace with instructions for each op code
 struct IrInstructionCast {
     IrInstruction base;
 
     IrInstruction *value;
-    IrInstruction *dest_type;
+    TypeTableEntry *dest_type;
     CastOp cast_op;
     LLVMValueRef tmp_ptr;
 };
@@ -1630,6 +1652,14 @@ struct IrInstructionContainerInitList {
     IrInstruction *container_type;
     size_t item_count;
     IrInstruction **items;
+    LLVMValueRef tmp_ptr;
+};
+
+struct IrInstructionContainerInitFieldsField {
+    Buf *name;
+    IrInstruction *value;
+    AstNode *source_node;
+    TypeStructField *type_struct_field;
 };
 
 struct IrInstructionContainerInitFields {
@@ -1637,8 +1667,21 @@ struct IrInstructionContainerInitFields {
 
     IrInstruction *container_type;
     size_t field_count;
-    Buf **field_names;
-    IrInstruction **field_values;
+    IrInstructionContainerInitFieldsField *fields;
+};
+
+struct IrInstructionStructInitField {
+    IrInstruction *value;
+    TypeStructField *type_struct_field;
+};
+
+struct IrInstructionStructInit {
+    IrInstruction base;
+
+    TypeTableEntry *struct_type;
+    size_t field_count;
+    IrInstructionStructInitField *fields;
+    LLVMValueRef tmp_ptr;
 };
 
 struct IrInstructionUnreachable {
@@ -1790,4 +1833,7 @@ static const size_t slice_len_index = 1;
 static const size_t maybe_child_index = 0;
 static const size_t maybe_null_index = 1;
 
+static const size_t enum_gen_tag_index = 0;
+static const size_t enum_gen_union_index = 1;
+
 #endif
src/analyze.cpp
@@ -84,46 +84,11 @@ AstNode *first_executing_node(AstNode *node) {
     zig_unreachable();
 }
 
-void mark_impure_fn(CodeGen *g, BlockContext *context, AstNode *node) {
-    if (!context->fn_entry) return;
-    if (!context->fn_entry->is_pure) return;
-
-    context->fn_entry->is_pure = false;
-    if (context->fn_entry->want_pure == WantPureTrue) {
-        context->fn_entry->proto_node->data.fn_proto.skip = true;
-
-        ErrorMsg *msg = add_node_error(g, context->fn_entry->proto_node,
-            buf_sprintf("failed to evaluate function at compile time"));
-
-        add_error_note(g, msg, node,
-            buf_sprintf("unable to evaluate this expression at compile time"));
-
-        if (context->fn_entry->want_pure_attr_node) {
-            add_error_note(g, msg, context->fn_entry->want_pure_attr_node,
-                buf_sprintf("required to be compile-time function here"));
-        }
-
-        if (context->fn_entry->want_pure_return_type) {
-            add_error_note(g, msg, context->fn_entry->want_pure_return_type,
-                buf_sprintf("required to be compile-time function because of return type '%s'",
-                buf_ptr(&context->fn_entry->type_entry->data.fn.fn_type_id.return_type->name)));
-        }
-    }
-}
-
 ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
     // if this assert fails, then parseh generated code that
     // failed semantic analysis, which isn't supposed to happen
     assert(!node->owner->c_import_node);
 
-    // if an error occurs in a function then it becomes impure
-    if (node->block_context) {
-        FnTableEntry *fn_entry = node->block_context->fn_entry;
-        if (fn_entry) {
-            fn_entry->is_pure = false;
-        }
-    }
-
     ErrorMsg *err = err_msg_create_with_line(node->owner->path, node->line, node->column,
             node->owner->source_code, node->owner->line_offsets, msg);
 
@@ -217,6 +182,7 @@ bool type_is_complete(TypeTableEntry *type_entry) {
         case TypeTableEntryIdNamespace:
         case TypeTableEntryIdBlock:
         case TypeTableEntryIdGenericFn:
+        case TypeTableEntryIdBoundFn:
             return true;
     }
     zig_unreachable();
@@ -241,7 +207,6 @@ TypeTableEntry *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x) {
 static TypeTableEntry *get_generic_fn_type(CodeGen *g, AstNode *decl_node) {
     TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdGenericFn);
     buf_init_from_str(&entry->name, "(generic function)");
-    entry->deep_const = true;
     entry->zero_bits = true;
     entry->data.generic_fn.decl_node = decl_node;
     return entry;
@@ -255,8 +220,6 @@ TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool
     } else {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdPointer);
 
-        entry->deep_const = is_const && child_type->deep_const;
-
         const char *const_str = is_const ? "const " : "";
         buf_resize(&entry->name, 0);
         buf_appendf(&entry->name, "&%s%s", const_str, buf_ptr(&child_type->name));
@@ -298,8 +261,6 @@ TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) {
         assert(child_type->type_ref);
         assert(child_type->di_type);
 
-        entry->deep_const = child_type->deep_const;
-
         buf_resize(&entry->name, 0);
         buf_appendf(&entry->name, "?%s", buf_ptr(&child_type->name));
 
@@ -383,8 +344,6 @@ TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) {
 
         entry->data.error.child_type = child_type;
 
-        entry->deep_const = child_type->deep_const;
-
         if (!type_has_bits(child_type)) {
             entry->type_ref = g->err_tag_type->type_ref;
             entry->di_type = g->err_tag_type->di_type;
@@ -456,7 +415,6 @@ TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdArray);
         entry->type_ref = child_type->type_ref ? LLVMArrayType(child_type->type_ref, array_size) : nullptr;
         entry->zero_bits = (array_size == 0) || child_type->zero_bits;
-        entry->deep_const = child_type->deep_const;
 
         buf_resize(&entry->name, 0);
         buf_appendf(&entry->name, "[%" PRIu64 "]%s", array_size, buf_ptr(&child_type->name));
@@ -507,8 +465,6 @@ TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *child_type, bool is_c
         TypeTableEntry *var_peer = get_slice_type(g, child_type, false);
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdStruct);
 
-        entry->deep_const = child_type->deep_const;
-
         buf_resize(&entry->name, 0);
         buf_appendf(&entry->name, "[]const %s", buf_ptr(&child_type->name));
 
@@ -657,7 +613,6 @@ TypeTableEntry *get_typedecl_type(CodeGen *g, const char *name, TypeTableEntry *
 
     buf_init_from_str(&entry->name, name);
 
-    entry->deep_const = child_type->deep_const;
     entry->type_ref = child_type->type_ref;
     entry->di_type = child_type->di_type;
     entry->zero_bits = child_type->zero_bits;
@@ -673,6 +628,23 @@ TypeTableEntry *get_typedecl_type(CodeGen *g, const char *name, TypeTableEntry *
     return entry;
 }
 
+TypeTableEntry *get_bound_fn_type(CodeGen *g, FnTableEntry *fn_entry) {
+    TypeTableEntry *fn_type = fn_entry->type_entry;
+    assert(fn_type->id == TypeTableEntryIdFn);
+    if (fn_type->data.fn.bound_fn_parent)
+        return fn_type->data.fn.bound_fn_parent;
+
+    TypeTableEntry *bound_fn_type = new_type_table_entry(TypeTableEntryIdBoundFn);
+    bound_fn_type->data.bound_fn.fn_type = fn_type;
+    bound_fn_type->zero_bits = true;
+
+    buf_resize(&bound_fn_type->name, 0);
+    buf_appendf(&bound_fn_type->name, "(bound %s)", buf_ptr(&fn_type->name));
+
+    fn_type->data.fn.bound_fn_parent = bound_fn_type;
+    return bound_fn_type;
+}
+
 // accepts ownership of fn_type_id memory
 TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id, bool gen_debug_info) {
     auto table_entry = g->fn_type_table.maybe_get(fn_type_id);
@@ -681,7 +653,6 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id, bool gen_debug_inf
     }
 
     TypeTableEntry *fn_type = new_type_table_entry(TypeTableEntryIdFn);
-    fn_type->deep_const = true;
     fn_type->data.fn.fn_type_id = *fn_type_id;
 
     if (fn_type_id->is_cold) {
@@ -838,6 +809,7 @@ static IrInstruction *analyze_const_value(CodeGen *g, BlockContext *scope, AstNo
         TypeTableEntry *expected_type)
 {
     IrExecutable ir_executable = {0};
+    ir_executable.is_inline = true;
     ir_gen(g, node, scope, &ir_executable);
 
     if (ir_executable.invalid)
@@ -851,6 +823,7 @@ static IrInstruction *analyze_const_value(CodeGen *g, BlockContext *scope, AstNo
         fprintf(stderr, "}\n");
     }
     IrExecutable analyzed_executable = {0};
+    analyzed_executable.is_inline = true;
     analyzed_executable.backward_branch_quota = default_backward_branch_quota;
     TypeTableEntry *result_type = ir_analyze(g, &ir_executable, &analyzed_executable, expected_type, node);
     if (result_type->id == TypeTableEntryIdInvalid)
@@ -884,8 +857,7 @@ static TypeTableEntry *analyze_type_expr(CodeGen *g, ImportTableEntry *import, B
 
 static bool fn_wants_full_static_eval(FnTableEntry *fn_table_entry) {
     assert(fn_table_entry);
-    AstNodeFnProto *fn_proto = &fn_table_entry->proto_node->data.fn_proto;
-    return fn_proto->inline_arg_count == fn_proto->params.length && fn_table_entry->want_pure == WantPureTrue;
+    return false;
 }
 
 // fn_table_entry is populated if and only if there is a function definition for this prototype
@@ -931,6 +903,7 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor
             case TypeTableEntryIdNamespace:
             case TypeTableEntryIdBlock:
             case TypeTableEntryIdGenericFn:
+            case TypeTableEntryIdBoundFn:
                 if (!fn_proto->skip) {
                     fn_proto->skip = true;
                     add_node_error(g, child->data.param_decl.type,
@@ -991,6 +964,7 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor
         case TypeTableEntryIdNamespace:
         case TypeTableEntryIdBlock:
         case TypeTableEntryIdGenericFn:
+        case TypeTableEntryIdBoundFn:
         case TypeTableEntryIdVar:
             if (!fn_proto->skip) {
                 fn_proto->skip = true;
@@ -1023,9 +997,6 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor
     }
 
     if (fn_table_entry && fn_type_id.return_type->id == TypeTableEntryIdMetaType) {
-        fn_table_entry->want_pure = WantPureTrue;
-        fn_table_entry->want_pure_return_type = fn_proto->return_type;
-
         ErrorMsg *err_msg = nullptr;
         for (size_t i = 0; i < fn_proto->params.length; i += 1) {
             AstNode *param_decl_node = fn_proto->params.at(i);
@@ -1046,8 +1017,7 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor
         }
     }
 
-
-    bool gen_debug_info = !(fn_table_entry && fn_wants_full_static_eval(fn_table_entry));
+    bool gen_debug_info = fn_table_entry && !fn_wants_full_static_eval(fn_table_entry);
     return get_fn_type(g, &fn_type_id, gen_debug_info);
 }
 
@@ -1167,8 +1137,6 @@ static void resolve_enum_type(CodeGen *g, ImportTableEntry *import, TypeTableEnt
     assert(decl_node->type == NodeTypeContainerDecl);
     assert(enum_type->di_type);
 
-    enum_type->deep_const = true;
-
     uint32_t field_count = decl_node->data.struct_decl.fields.length;
 
     enum_type->data.enumeration.src_field_count = field_count;
@@ -1198,11 +1166,6 @@ static void resolve_enum_type(CodeGen *g, ImportTableEntry *import, TypeTableEnt
         type_enum_field->type_entry = field_type;
         type_enum_field->value = i;
 
-        if (!field_type->deep_const) {
-            enum_type->deep_const = false;
-        }
-
-
         di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(type_enum_field->name), i);
 
         if (field_type->id == TypeTableEntryIdStruct) {
@@ -1362,8 +1325,6 @@ static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableE
     assert(decl_node->type == NodeTypeContainerDecl);
     assert(struct_type->di_type);
 
-    struct_type->deep_const = true;
-
     size_t field_count = decl_node->data.struct_decl.fields.length;
 
     struct_type->data.structure.src_field_count = field_count;
@@ -1389,10 +1350,6 @@ static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableE
         type_struct_field->src_index = i;
         type_struct_field->gen_index = SIZE_MAX;
 
-        if (!field_type->deep_const) {
-            struct_type->deep_const = false;
-        }
-
         if (field_type->id == TypeTableEntryIdStruct) {
             resolve_struct_type(g, import, field_type);
         } else if (field_type->id == TypeTableEntryIdEnum) {
@@ -1537,7 +1494,6 @@ static void preview_fn_proto_instance(CodeGen *g, ImportTableEntry *import, AstN
     fn_table_entry->proto_node = proto_node;
     fn_table_entry->fn_def_node = fn_def_node;
     fn_table_entry->is_extern = is_extern;
-    fn_table_entry->is_pure = fn_def_node != nullptr;
 
     get_fully_qualified_decl_name(&fn_table_entry->symbol_name, proto_node, '_');
 
@@ -1852,6 +1808,7 @@ TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEnt
         case TypeTableEntryIdUnion:
         case TypeTableEntryIdFn:
         case TypeTableEntryIdGenericFn:
+        case TypeTableEntryIdBoundFn:
             return type_entry;
     }
     zig_unreachable();
@@ -2100,6 +2057,7 @@ static bool type_has_codegen_value(TypeTableEntry *type_entry) {
         case TypeTableEntryIdNamespace:
         case TypeTableEntryIdBlock:
         case TypeTableEntryIdGenericFn:
+        case TypeTableEntryIdBoundFn:
             return false;
 
         case TypeTableEntryIdBool:
@@ -2314,6 +2272,7 @@ static bool is_container(TypeTableEntry *type_entry) {
         case TypeTableEntryIdNamespace:
         case TypeTableEntryIdBlock:
         case TypeTableEntryIdGenericFn:
+        case TypeTableEntryIdBoundFn:
             return false;
     }
     zig_unreachable();
@@ -2361,6 +2320,7 @@ void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry) {
         case TypeTableEntryIdNamespace:
         case TypeTableEntryIdBlock:
         case TypeTableEntryIdGenericFn:
+        case TypeTableEntryIdBoundFn:
         case TypeTableEntryIdInvalid:
         case TypeTableEntryIdVar:
             zig_unreachable();
@@ -2429,10 +2389,6 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {
         if (fn_type->data.fn.gen_param_info) {
             var->gen_arg_index = fn_type->data.fn.gen_param_info[i].gen_index;
         }
-
-        if (!type->deep_const) {
-            fn_table_entry->is_pure = false;
-        }
     }
 
     TypeTableEntry *expected_type = fn_type->data.fn.fn_type_id.return_type;
@@ -2768,6 +2724,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {
         case TypeTableEntryIdNamespace:
         case TypeTableEntryIdBlock:
         case TypeTableEntryIdGenericFn:
+        case TypeTableEntryIdBoundFn:
         case TypeTableEntryIdVar:
              zig_unreachable();
         case TypeTableEntryIdUnreachable:
@@ -2820,6 +2777,14 @@ static uint32_t hash_size(size_t x) {
     return x % UINT32_MAX;
 }
 
+uint32_t fn_table_entry_hash(FnTableEntry* value) {
+    return ptr_hash(value);
+}
+
+bool fn_table_entry_eql(FnTableEntry *a, FnTableEntry *b) {
+    return ptr_eq(a, b);
+}
+
 uint32_t fn_type_id_hash(FnTypeId *id) {
     uint32_t result = 0;
     result += id->is_extern ? 3349388391 : 0;
@@ -2912,6 +2877,7 @@ static uint32_t hash_const_val(TypeTableEntry *type, ConstExprValue *const_val)
         case TypeTableEntryIdBlock:
             return hash_ptr(const_val->data.x_block);
         case TypeTableEntryIdGenericFn:
+        case TypeTableEntryIdBoundFn:
         case TypeTableEntryIdInvalid:
         case TypeTableEntryIdUnreachable:
         case TypeTableEntryIdVar:
@@ -2990,6 +2956,7 @@ static TypeTableEntry *type_of_first_thing_in_memory(TypeTableEntry *type_entry)
         case TypeTableEntryIdNamespace:
         case TypeTableEntryIdBlock:
         case TypeTableEntryIdGenericFn:
+        case TypeTableEntryIdBoundFn:
         case TypeTableEntryIdVar:
             zig_unreachable();
         case TypeTableEntryIdArray:
src/analyze.hpp
@@ -31,6 +31,7 @@ TypeTableEntry *get_partial_container_type(CodeGen *g, ImportTableEntry *import,
         ContainerKind kind, AstNode *decl_node, const char *name);
 TypeTableEntry *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x);
 TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type);
+TypeTableEntry *get_bound_fn_type(CodeGen *g, FnTableEntry *fn_entry);
 bool handle_is_ptr(TypeTableEntry *type_entry);
 void find_libc_include_path(CodeGen *g);
 void find_libc_lib_path(CodeGen *g);
@@ -52,7 +53,6 @@ VariableTableEntry *find_variable(CodeGen *g, BlockContext *orig_context, Buf *n
 AstNode *find_decl(BlockContext *context, Buf *name);
 void resolve_top_level_decl(CodeGen *g, AstNode *node, bool pointer_only);
 TopLevelDecl *get_as_top_level_decl(AstNode *node);
-void mark_impure_fn(CodeGen *g, BlockContext *context, AstNode *node);
 bool type_is_codegen_pointer(TypeTableEntry *type);
 TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEntry *type_entry);
 TypeTableEntry *container_ref_type(TypeTableEntry *type_entry);
src/codegen.cpp
@@ -1368,27 +1368,34 @@ static LLVMValueRef ir_render_elem_ptr(CodeGen *g, IrExecutable *executable, IrI
 }
 
 static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstructionCall *instruction) {
-    LLVMValueRef fn_val = ir_llvm_value(g, instruction->fn);
-    TypeTableEntry *fn_type = instruction->fn->type_entry;
+    LLVMValueRef fn_val;
+    TypeTableEntry *fn_type;
+    if (instruction->fn_entry) {
+        fn_val = instruction->fn_entry->fn_value;
+        fn_type = instruction->fn_entry->type_entry;
+    } else {
+        assert(instruction->fn_ref);
+        fn_val = ir_llvm_value(g, instruction->fn_ref);
+        fn_type = instruction->fn_ref->type_entry;
+    }
+
     TypeTableEntry *src_return_type = fn_type->data.fn.fn_type_id.return_type;
     bool ret_has_bits = type_has_bits(src_return_type);
-    size_t fn_call_param_count = instruction->arg_count;
     bool first_arg_ret = ret_has_bits && handle_is_ptr(src_return_type);
-    size_t actual_param_count = fn_call_param_count + (first_arg_ret ? 1 : 0);
+    size_t actual_param_count = instruction->arg_count + (first_arg_ret ? 1 : 0);
     bool is_var_args = fn_type->data.fn.fn_type_id.is_var_args;
     LLVMValueRef *gen_param_values = allocate<LLVMValueRef>(actual_param_count);
     size_t gen_param_index = 0;
     if (first_arg_ret) {
-        zig_panic("TODO");
-        //gen_param_values[gen_param_index] = node->data.fn_call_expr.tmp_ptr;
-        //gen_param_index += 1;
+        gen_param_values[gen_param_index] = instruction->tmp_ptr;
+        gen_param_index += 1;
     }
-    for (size_t call_i = 0; call_i < fn_call_param_count; call_i += 1) {
+    for (size_t call_i = 0; call_i < instruction->arg_count; call_i += 1) {
         IrInstruction *param_instruction = instruction->args[call_i];
-        LLVMValueRef param_value = ir_llvm_value(g, param_instruction);
-        assert(param_value);
         TypeTableEntry *param_type = param_instruction->type_entry;
         if (is_var_args || type_has_bits(param_type)) {
+            LLVMValueRef param_value = ir_llvm_value(g, param_instruction);
+            assert(param_value);
             gen_param_values[gen_param_index] = param_value;
             gen_param_index += 1;
         }
@@ -1402,8 +1409,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
     } else if (!ret_has_bits) {
         return nullptr;
     } else if (first_arg_ret) {
-        zig_panic("TODO");
-        //return node->data.fn_call_expr.tmp_ptr;
+        return instruction->tmp_ptr;
     } else {
         return result;
     }
@@ -1422,6 +1428,22 @@ static LLVMValueRef ir_render_struct_field_ptr(CodeGen *g, IrExecutable *executa
     return LLVMBuildStructGEP(g->builder, struct_ptr, field->gen_index, "");
 }
 
+static LLVMValueRef ir_render_enum_field_ptr(CodeGen *g, IrExecutable *executable,
+    IrInstructionEnumFieldPtr *instruction)
+{
+    LLVMValueRef enum_ptr = ir_llvm_value(g, instruction->enum_ptr);
+    TypeEnumField *field = instruction->field;
+
+    if (!type_has_bits(field->type_entry))
+        return nullptr;
+
+    LLVMTypeRef field_type_ref = LLVMPointerType(field->type_entry->type_ref, 0);
+    LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, enum_ptr, enum_gen_union_index, "");
+    LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr, field_type_ref, "");
+
+    return bitcasted_union_field_ptr;
+}
+
 static size_t find_asm_index(CodeGen *g, AstNode *node, AsmToken *tok) {
     const char *ptr = buf_ptr(node->data.asm_expr.asm_template) + tok->start + 2;
     size_t len = tok->end - tok->start - 2;
@@ -1691,6 +1713,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
         case IrInstructionIdSwitchTarget:
         case IrInstructionIdStaticEval:
         case IrInstructionIdImport:
+        case IrInstructionIdContainerInitFields:
             zig_unreachable();
         case IrInstructionIdReturn:
             return ir_render_return(g, executable, (IrInstructionReturn *)instruction);
@@ -1720,6 +1743,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
             return ir_render_call(g, executable, (IrInstructionCall *)instruction);
         case IrInstructionIdStructFieldPtr:
             return ir_render_struct_field_ptr(g, executable, (IrInstructionStructFieldPtr *)instruction);
+        case IrInstructionIdEnumFieldPtr:
+            return ir_render_enum_field_ptr(g, executable, (IrInstructionEnumFieldPtr *)instruction);
         case IrInstructionIdAsm:
             return ir_render_asm(g, executable, (IrInstructionAsm *)instruction);
         case IrInstructionIdTestNull:
@@ -1738,7 +1763,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
             return ir_render_ref(g, executable, (IrInstructionRef *)instruction);
         case IrInstructionIdSwitchVar:
         case IrInstructionIdContainerInitList:
-        case IrInstructionIdContainerInitFields:
+        case IrInstructionIdStructInit:
         case IrInstructionIdEnumTag:
         case IrInstructionIdArrayLen:
             zig_panic("TODO render more IR instructions to LLVM");
@@ -1960,6 +1985,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, TypeTableEntry *type_entry, ConstE
         case TypeTableEntryIdNamespace:
         case TypeTableEntryIdBlock:
         case TypeTableEntryIdGenericFn:
+        case TypeTableEntryIdBoundFn:
         case TypeTableEntryIdVar:
             zig_unreachable();
 
@@ -2197,7 +2223,6 @@ static void do_code_gen(CodeGen *g) {
 
         TypeTableEntry *fn_type = fn_table_entry->type_entry;
 
-        bool is_sret = false;
         if (!type_has_bits(fn_type->data.fn.fn_type_id.return_type)) {
             // nothing to do
         } else if (fn_type->data.fn.fn_type_id.return_type->id == TypeTableEntryIdPointer) {
@@ -2208,10 +2233,6 @@ static void do_code_gen(CodeGen *g) {
             LLVMValueRef first_arg = LLVMGetParam(fn_table_entry->fn_value, 0);
             LLVMAddAttribute(first_arg, LLVMStructRetAttribute);
             ZigLLVMAddNonNullAttr(fn_table_entry->fn_value, 1);
-            is_sret = true;
-        }
-        if (fn_table_entry->is_pure && !is_sret && g->is_release_build) {
-            LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMReadOnlyAttribute);
         }
 
 
@@ -2234,9 +2255,7 @@ static void do_code_gen(CodeGen *g) {
             if (param_is_noalias) {
                 LLVMAddAttribute(argument_val, LLVMNoAliasAttribute);
             }
-            if ((param_type->id == TypeTableEntryIdPointer && (param_type->data.pointer.is_const || fn_table_entry->is_pure)) ||
-                is_byval)
-            {
+            if ((param_type->id == TypeTableEntryIdPointer && param_type->data.pointer.is_const) || is_byval) {
                 LLVMAddAttribute(argument_val, LLVMReadOnlyAttribute);
             }
             if (param_type->id == TypeTableEntryIdPointer) {
@@ -2339,6 +2358,15 @@ static void do_code_gen(CodeGen *g) {
             } else if (instruction->id == IrInstructionIdRef) {
                 IrInstructionRef *ref_instruction = (IrInstructionRef *)instruction;
                 slot = &ref_instruction->tmp_ptr;
+            } else if (instruction->id == IrInstructionIdContainerInitList) {
+                IrInstructionContainerInitList *container_init_list_instruction = (IrInstructionContainerInitList *)instruction;
+                slot = &container_init_list_instruction->tmp_ptr;
+            } else if (instruction->id == IrInstructionIdStructInit) {
+                IrInstructionStructInit *struct_init_instruction = (IrInstructionStructInit *)instruction;
+                slot = &struct_init_instruction->tmp_ptr;
+            } else if (instruction->id == IrInstructionIdCall) {
+                IrInstructionCall *call_instruction = (IrInstructionCall *)instruction;
+                slot = &call_instruction->tmp_ptr;
             } else {
                 zig_unreachable();
             }
@@ -2464,46 +2492,39 @@ static void define_builtin_types(CodeGen *g) {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNamespace);
         buf_init_from_str(&entry->name, "(namespace)");
         entry->zero_bits = true;
-        entry->deep_const = true;
         g->builtin_types.entry_namespace = entry;
     }
     {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdBlock);
         buf_init_from_str(&entry->name, "(block)");
         entry->zero_bits = true;
-        entry->deep_const = true;
         g->builtin_types.entry_block = entry;
     }
     {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNumLitFloat);
         buf_init_from_str(&entry->name, "(float literal)");
         entry->zero_bits = true;
-        entry->deep_const = true;
         g->builtin_types.entry_num_lit_float = entry;
     }
     {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNumLitInt);
         buf_init_from_str(&entry->name, "(integer literal)");
         entry->zero_bits = true;
-        entry->deep_const = true;
         g->builtin_types.entry_num_lit_int = entry;
     }
     {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdUndefLit);
         buf_init_from_str(&entry->name, "(undefined)");
-        entry->deep_const = true;
         g->builtin_types.entry_undef = entry;
     }
     {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNullLit);
         buf_init_from_str(&entry->name, "(null)");
-        entry->deep_const = true;
         g->builtin_types.entry_null = entry;
     }
     {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdVar);
         buf_init_from_str(&entry->name, "(var)");
-        entry->deep_const = true;
         g->builtin_types.entry_var = entry;
     }
 
@@ -2514,7 +2535,6 @@ static void define_builtin_types(CodeGen *g) {
 
             TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
             entry->type_ref = LLVMIntType(size_in_bits);
-            entry->deep_const = true;
 
             const char u_or_i = is_signed ? 'i' : 'u';
             buf_resize(&entry->name, 0);
@@ -2554,7 +2574,6 @@ static void define_builtin_types(CodeGen *g) {
 
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
         entry->type_ref = LLVMIntType(size_in_bits);
-        entry->deep_const = true;
 
         buf_init_from_str(&entry->name, info->name);
 
@@ -2574,7 +2593,6 @@ static void define_builtin_types(CodeGen *g) {
     {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdBool);
         entry->type_ref = LLVMInt1Type();
-        entry->deep_const = true;
         buf_init_from_str(&entry->name, "bool");
         uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref);
         uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, entry->type_ref);
@@ -2590,7 +2608,6 @@ static void define_builtin_types(CodeGen *g) {
         bool is_signed = is_signed_list[sign_i];
 
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
-        entry->deep_const = true;
         entry->type_ref = LLVMIntType(g->pointer_size_bytes * 8);
 
         const char u_or_i = is_signed ? 'i' : 'u';
@@ -2616,7 +2633,6 @@ static void define_builtin_types(CodeGen *g) {
     }
     {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat);
-        entry->deep_const = true;
         entry->type_ref = LLVMFloatType();
         buf_init_from_str(&entry->name, "f32");
         entry->data.floating.bit_count = 32;
@@ -2632,7 +2648,6 @@ static void define_builtin_types(CodeGen *g) {
     }
     {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat);
-        entry->deep_const = true;
         entry->type_ref = LLVMDoubleType();
         buf_init_from_str(&entry->name, "f64");
         entry->data.floating.bit_count = 64;
@@ -2648,7 +2663,6 @@ static void define_builtin_types(CodeGen *g) {
     }
     {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat);
-        entry->deep_const = true;
         entry->type_ref = LLVMX86FP80Type();
         buf_init_from_str(&entry->name, "c_long_double");
         entry->data.floating.bit_count = 80;
@@ -2664,7 +2678,6 @@ static void define_builtin_types(CodeGen *g) {
     }
     {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdVoid);
-        entry->deep_const = true;
         entry->type_ref = LLVMVoidType();
         entry->zero_bits = true;
         buf_init_from_str(&entry->name, "void");
@@ -2677,7 +2690,6 @@ static void define_builtin_types(CodeGen *g) {
     }
     {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdUnreachable);
-        entry->deep_const = true;
         entry->type_ref = LLVMVoidType();
         entry->zero_bits = true;
         buf_init_from_str(&entry->name, "unreachable");
@@ -2687,7 +2699,6 @@ static void define_builtin_types(CodeGen *g) {
     }
     {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdMetaType);
-        entry->deep_const = true;
         buf_init_from_str(&entry->name, "type");
         entry->zero_bits = true;
         g->builtin_types.entry_type = entry;
@@ -2710,7 +2721,6 @@ static void define_builtin_types(CodeGen *g) {
 
     {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdPureError);
-        entry->deep_const = true;
         buf_init_from_str(&entry->name, "error");
 
         // TODO allow overriding this type and keep track of max value and emit an
@@ -2726,7 +2736,6 @@ static void define_builtin_types(CodeGen *g) {
 
     {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum);
-        entry->deep_const = true;
         entry->zero_bits = true; // only allowed at compile time
         buf_init_from_str(&entry->name, "@OS");
         uint32_t field_count = target_os_count();
@@ -2752,7 +2761,6 @@ static void define_builtin_types(CodeGen *g) {
 
     {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum);
-        entry->deep_const = true;
         entry->zero_bits = true; // only allowed at compile time
         buf_init_from_str(&entry->name, "@Arch");
         uint32_t field_count = target_arch_count();
@@ -2784,7 +2792,6 @@ static void define_builtin_types(CodeGen *g) {
 
     {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum);
-        entry->deep_const = true;
         entry->zero_bits = true; // only allowed at compile time
         buf_init_from_str(&entry->name, "@Environ");
         uint32_t field_count = target_environ_count();
@@ -2811,7 +2818,6 @@ static void define_builtin_types(CodeGen *g) {
 
     {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum);
-        entry->deep_const = true;
         entry->zero_bits = true; // only allowed at compile time
         buf_init_from_str(&entry->name, "@ObjectFormat");
         uint32_t field_count = target_oformat_count();
@@ -2838,7 +2844,6 @@ static void define_builtin_types(CodeGen *g) {
 
     {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum);
-        entry->deep_const = true;
         buf_init_from_str(&entry->name, "AtomicOrder");
         uint32_t field_count = 6;
         entry->data.enumeration.src_field_count = field_count;
@@ -2998,7 +3003,6 @@ static void define_builtin_fns(CodeGen *g) {
     create_builtin_fn_with_arg_count(g, BuiltinFnIdUnreachable, "unreachable", 0);
     create_builtin_fn_with_arg_count(g, BuiltinFnIdSetFnTest, "setFnTest", 2);
     create_builtin_fn_with_arg_count(g, BuiltinFnIdSetFnVisible, "setFnVisible", 2);
-    create_builtin_fn_with_arg_count(g, BuiltinFnIdSetFnStaticEval, "setFnStaticEval", 2);
     create_builtin_fn_with_arg_count(g, BuiltinFnIdSetFnNoInline, "setFnNoInline", 2);
     create_builtin_fn_with_arg_count(g, BuiltinFnIdSetDebugSafety, "setDebugSafety", 2);
 }
@@ -3287,6 +3291,7 @@ static void get_c_type(CodeGen *g, TypeTableEntry *type_entry, Buf *out_buf) {
         case TypeTableEntryIdInvalid:
         case TypeTableEntryIdMetaType:
         case TypeTableEntryIdGenericFn:
+        case TypeTableEntryIdBoundFn:
         case TypeTableEntryIdNamespace:
         case TypeTableEntryIdBlock:
         case TypeTableEntryIdNumLitFloat:
src/eval.cpp
@@ -56,6 +56,7 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b, TypeTableEntry *ty
         case TypeTableEntryIdBlock:
             zig_panic("TODO");
         case TypeTableEntryIdGenericFn:
+        case TypeTableEntryIdBoundFn:
         case TypeTableEntryIdInvalid:
         case TypeTableEntryIdUnreachable:
         case TypeTableEntryIdVar:
src/ir.cpp
@@ -49,6 +49,10 @@ ConstExprValue *const_ptr_pointee(ConstExprValue *const_val) {
     }
 }
 
+static bool ir_should_inline(IrBuilder *irb) {
+    return irb->exec->is_inline;
+}
+
 static void ir_instruction_append(IrBasicBlock *basic_block, IrInstruction *instruction) {
     assert(basic_block);
     assert(instruction);
@@ -160,6 +164,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionStructFieldPtr *
     return IrInstructionIdStructFieldPtr;
 }
 
+static constexpr IrInstructionId ir_instruction_id(IrInstructionEnumFieldPtr *) {
+    return IrInstructionIdEnumFieldPtr;
+}
+
 static constexpr IrInstructionId ir_instruction_id(IrInstructionElemPtr *) {
     return IrInstructionIdElemPtr;
 }
@@ -276,6 +284,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionRef *) {
     return IrInstructionIdRef;
 }
 
+static constexpr IrInstructionId ir_instruction_id(IrInstructionStructInit *) {
+    return IrInstructionIdStructInit;
+}
+
 template<typename T>
 static T *ir_create_instruction(IrExecutable *exec, AstNode *source_node) {
     T *special_instruction = allocate<T>(1);
@@ -293,15 +305,14 @@ static T *ir_build_instruction(IrBuilder *irb, AstNode *source_node) {
     return special_instruction;
 }
 
-static IrInstruction *ir_build_cast(IrBuilder *irb, AstNode *source_node, IrInstruction *dest_type,
-        IrInstruction *value, CastOp cast_op)
+static IrInstruction *ir_build_cast(IrBuilder *irb, AstNode *source_node, TypeTableEntry *dest_type,
+    IrInstruction *value, CastOp cast_op)
 {
     IrInstructionCast *cast_instruction = ir_build_instruction<IrInstructionCast>(irb, source_node);
     cast_instruction->dest_type = dest_type;
     cast_instruction->value = value;
     cast_instruction->cast_op = cast_op;
 
-    ir_ref_instruction(dest_type);
     ir_ref_instruction(value);
 
     return &cast_instruction->base;
@@ -353,10 +364,14 @@ static IrInstruction *ir_build_return_from(IrBuilder *irb, IrInstruction *old_in
     return new_instruction;
 }
 
-static IrInstruction *ir_create_const(IrBuilder *irb, AstNode *source_node, TypeTableEntry *type_entry) {
+static IrInstruction *ir_create_const(IrBuilder *irb, AstNode *source_node,
+    TypeTableEntry *type_entry, bool depends_on_compile_var)
+{
+    assert(type_entry);
     IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(irb->exec, source_node);
     const_instruction->base.type_entry = type_entry;
     const_instruction->base.static_value.special = ConstValSpecialStatic;
+    const_instruction->base.static_value.depends_on_compile_var = depends_on_compile_var;
     return &const_instruction->base;
 }
 
@@ -452,6 +467,18 @@ static IrInstruction *ir_build_const_bool(IrBuilder *irb, AstNode *source_node,
     return &const_instruction->base;
 }
 
+static IrInstruction *ir_build_const_bound_fn(IrBuilder *irb, AstNode *source_node,
+    FnTableEntry *fn_entry, IrInstruction *first_arg, bool depends_on_compile_var)
+{
+    IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, source_node);
+    const_instruction->base.type_entry = get_bound_fn_type(irb->codegen, fn_entry);
+    const_instruction->base.static_value.special = ConstValSpecialStatic;
+    const_instruction->base.static_value.depends_on_compile_var = depends_on_compile_var;
+    const_instruction->base.static_value.data.x_bound_fn.fn = fn_entry;
+    const_instruction->base.static_value.data.x_bound_fn.first_arg = first_arg;
+    return &const_instruction->base;
+}
+
 static IrInstruction *ir_build_const_str_lit(IrBuilder *irb, AstNode *source_node, Buf *str) {
     IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, source_node);
     TypeTableEntry *u8_type = irb->codegen->builtin_types.entry_u8;
@@ -595,26 +622,48 @@ static IrInstruction *ir_build_struct_field_ptr_from(IrBuilder *irb, IrInstructi
     return new_instruction;
 }
 
+static IrInstruction *ir_build_enum_field_ptr(IrBuilder *irb, AstNode *source_node,
+    IrInstruction *enum_ptr, TypeEnumField *field)
+{
+    IrInstructionEnumFieldPtr *instruction = ir_build_instruction<IrInstructionEnumFieldPtr>(irb, source_node);
+    instruction->enum_ptr = enum_ptr;
+    instruction->field = field;
+
+    ir_ref_instruction(enum_ptr);
+
+    return &instruction->base;
+}
+
+static IrInstruction *ir_build_enum_field_ptr_from(IrBuilder *irb, IrInstruction *old_instruction,
+    IrInstruction *enum_ptr, TypeEnumField *type_enum_field)
+{
+    IrInstruction *new_instruction = ir_build_enum_field_ptr(irb, old_instruction->source_node,
+        enum_ptr, type_enum_field);
+    ir_link_new_instruction(new_instruction, old_instruction);
+    return new_instruction;
+}
+
 static IrInstruction *ir_build_call(IrBuilder *irb, AstNode *source_node,
-        IrInstruction *fn, size_t arg_count, IrInstruction **args)
+        FnTableEntry *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args)
 {
     IrInstructionCall *call_instruction = ir_build_instruction<IrInstructionCall>(irb, source_node);
-    call_instruction->fn = fn;
+    call_instruction->fn_entry = fn_entry;
+    call_instruction->fn_ref = fn_ref;
     call_instruction->arg_count = arg_count;
     call_instruction->args = args;
 
-    ir_ref_instruction(fn);
-    for (size_t i = 0; i < arg_count; i += 1) {
+    if (fn_ref)
+        ir_ref_instruction(fn_ref);
+    for (size_t i = 0; i < arg_count; i += 1)
         ir_ref_instruction(args[i]);
-    }
 
     return &call_instruction->base;
 }
 
 static IrInstruction *ir_build_call_from(IrBuilder *irb, IrInstruction *old_instruction,
-        IrInstruction *fn, size_t arg_count, IrInstruction **args)
+        FnTableEntry *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args)
 {
-    IrInstruction *new_instruction = ir_build_call(irb, old_instruction->source_node, fn, arg_count, args);
+    IrInstruction *new_instruction = ir_build_call(irb, old_instruction->source_node, fn_entry, fn_ref, arg_count, args);
     ir_link_new_instruction(new_instruction, old_instruction);
     return new_instruction;
 }
@@ -706,24 +755,55 @@ static IrInstruction *ir_build_container_init_list(IrBuilder *irb, AstNode *sour
     return &container_init_list_instruction->base;
 }
 
+static IrInstruction *ir_build_container_init_list_from(IrBuilder *irb, IrInstruction *old_instruction,
+        IrInstruction *container_type, size_t item_count, IrInstruction **items)
+{
+    IrInstruction *new_instruction = ir_build_container_init_list(irb, old_instruction->source_node,
+        container_type, item_count, items);
+    ir_link_new_instruction(new_instruction, old_instruction);
+    return new_instruction;
+}
+
 static IrInstruction *ir_build_container_init_fields(IrBuilder *irb, AstNode *source_node,
-        IrInstruction *container_type, size_t field_count, Buf **field_names, IrInstruction **field_values)
+        IrInstruction *container_type, size_t field_count, IrInstructionContainerInitFieldsField *fields)
 {
     IrInstructionContainerInitFields *container_init_fields_instruction =
         ir_build_instruction<IrInstructionContainerInitFields>(irb, source_node);
     container_init_fields_instruction->container_type = container_type;
     container_init_fields_instruction->field_count = field_count;
-    container_init_fields_instruction->field_names = field_names;
-    container_init_fields_instruction->field_values = field_values;
+    container_init_fields_instruction->fields = fields;
 
     ir_ref_instruction(container_type);
     for (size_t i = 0; i < field_count; i += 1) {
-        ir_ref_instruction(field_values[i]);
+        ir_ref_instruction(fields[i].value);
     }
 
     return &container_init_fields_instruction->base;
 }
 
+static IrInstruction *ir_build_struct_init(IrBuilder *irb, AstNode *source_node,
+        TypeTableEntry *struct_type, size_t field_count, IrInstructionStructInitField *fields)
+{
+    IrInstructionStructInit *struct_init_instruction = ir_build_instruction<IrInstructionStructInit>(irb, source_node);
+    struct_init_instruction->struct_type = struct_type;
+    struct_init_instruction->field_count = field_count;
+    struct_init_instruction->fields = fields;
+
+    for (size_t i = 0; i < field_count; i += 1)
+        ir_ref_instruction(fields[i].value);
+
+    return &struct_init_instruction->base;
+}
+
+static IrInstruction *ir_build_struct_init_from(IrBuilder *irb, IrInstruction *old_instruction,
+        TypeTableEntry *struct_type, size_t field_count, IrInstructionStructInitField *fields)
+{
+    IrInstruction *new_instruction = ir_build_struct_init(irb, old_instruction->source_node,
+        struct_type, field_count, fields);
+    ir_link_new_instruction(new_instruction, old_instruction);
+    return new_instruction;
+}
+
 static IrInstruction *ir_build_unreachable(IrBuilder *irb, AstNode *source_node) {
     IrInstructionUnreachable *unreachable_instruction =
         ir_build_instruction<IrInstructionUnreachable>(irb, source_node);
@@ -1442,7 +1522,7 @@ static IrInstruction *ir_gen_decl_ref(IrBuilder *irb, AstNode *source_node, AstN
             ref_instruction = ir_build_const_fn(irb, source_node, fn_entry);
         }
         if (lval != LValPurposeNone)
-            return ir_build_un_op(irb, source_node, IrUnOpAddressOf, ref_instruction);
+            return ir_build_ref(irb, source_node, ref_instruction);
         else
             return ref_instruction;
     } else if (decl_node->type == NodeTypeContainerDecl) {
@@ -1455,14 +1535,14 @@ static IrInstruction *ir_gen_decl_ref(IrBuilder *irb, AstNode *source_node, AstN
             ref_instruction = ir_build_const_type(irb, source_node, decl_node->data.struct_decl.type_entry);
         }
         if (lval != LValPurposeNone)
-            return ir_build_un_op(irb, source_node, IrUnOpAddressOf, ref_instruction);
+            return ir_build_ref(irb, source_node, ref_instruction);
         else
             return ref_instruction;
     } else if (decl_node->type == NodeTypeTypeDecl) {
         TypeTableEntry *child_type = decl_node->data.type_decl.child_type_entry;
         IrInstruction *ref_instruction = ir_build_const_type(irb, source_node, child_type);
         if (lval != LValPurposeNone)
-            return ir_build_un_op(irb, source_node, IrUnOpAddressOf, ref_instruction);
+            return ir_build_ref(irb, source_node, ref_instruction);
         else
             return ref_instruction;
     } else {
@@ -1712,7 +1792,6 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, AstNode *node) {
         case BuiltinFnIdDivExact:
         case BuiltinFnIdTruncate:
         case BuiltinFnIdIntType:
-        case BuiltinFnIdSetFnStaticEval:
         case BuiltinFnIdSetFnNoInline:
             zig_panic("TODO IR gen more builtin functions");
     }
@@ -1726,9 +1805,9 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, AstNode *node) {
         return ir_gen_builtin_fn_call(irb, node);
 
     AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr;
-    IrInstruction *fn = ir_gen_node(irb, fn_ref_node, node->block_context);
-    if (fn == irb->codegen->invalid_instruction)
-        return fn;
+    IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, node->block_context);
+    if (fn_ref == irb->codegen->invalid_instruction)
+        return fn_ref;
 
     size_t arg_count = node->data.fn_call_expr.params.length;
     IrInstruction **args = allocate<IrInstruction*>(arg_count);
@@ -1737,7 +1816,7 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, AstNode *node) {
         args[i] = ir_gen_node(irb, arg_node, node->block_context);
     }
 
-    return ir_build_call(irb, node, fn, arg_count, args);
+    return ir_build_call(irb, node, nullptr, fn_ref, arg_count, args);
 }
 
 static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, AstNode *node) {
@@ -1754,7 +1833,7 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, AstNode *node) {
     IrBasicBlock *else_block = ir_build_basic_block(irb, "Else");
     IrBasicBlock *endif_block = ir_build_basic_block(irb, "EndIf");
 
-    bool is_inline = (node->block_context->fn_entry == nullptr);
+    bool is_inline = ir_should_inline(irb) || node->data.if_bool_expr.is_inline;
     ir_build_cond_br(irb, condition->source_node, condition, then_block, else_block, is_inline);
 
     ir_set_cursor_at_end(irb, then_block);
@@ -1865,8 +1944,7 @@ static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, AstNode *node)
 
     if (kind == ContainerInitKindStruct) {
         size_t field_count = container_init_expr->entries.length;
-        IrInstruction **values = allocate<IrInstruction *>(field_count);
-        Buf **names = allocate<Buf *>(field_count);
+        IrInstructionContainerInitFieldsField *fields = allocate<IrInstructionContainerInitFieldsField>(field_count);
         for (size_t i = 0; i < field_count; i += 1) {
             AstNode *entry_node = container_init_expr->entries.at(i);
             assert(entry_node->type == NodeTypeStructValueField);
@@ -1877,10 +1955,11 @@ static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, AstNode *node)
             if (expr_value == irb->codegen->invalid_instruction)
                 return expr_value;
 
-            names[i] = name;
-            values[i] = expr_value;
+            fields[i].name = name;
+            fields[i].value = expr_value;
+            fields[i].source_node = entry_node;
         }
-        return ir_build_container_init_fields(irb, node, container_type, field_count, names, values);
+        return ir_build_container_init_fields(irb, node, container_type, field_count, fields);
     } else if (kind == ContainerInitKindArray) {
         size_t item_count = container_init_expr->entries.length;
         IrInstruction **values = allocate<IrInstruction *>(item_count);
@@ -1919,7 +1998,7 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, AstNode *node) {
     bool is_shadowable = false;
     bool is_const = variable_declaration->is_const;
     bool is_extern = variable_declaration->is_extern;
-    bool is_inline = variable_declaration->is_inline;
+    bool is_inline = ir_should_inline(irb) || variable_declaration->is_inline;
     VariableTableEntry *var = ir_add_local_var(irb, node, node->block_context,
             variable_declaration->symbol, is_const, is_const, is_shadowable, is_inline);
 
@@ -1943,7 +2022,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, AstNode *node) {
         ir_build_basic_block(irb, "WhileContinue") : cond_block;
     IrBasicBlock *end_block = ir_build_basic_block(irb, "WhileEnd");
 
-    bool is_inline = node->data.while_expr.is_inline;
+    bool is_inline = ir_should_inline(irb) || node->data.while_expr.is_inline;
     ir_build_br(irb, node, cond_block, is_inline);
 
     if (continue_expr_node) {
@@ -1998,7 +2077,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, AstNode *node) {
     } else {
         elem_var_type = ir_build_ptr_type_child(irb, elem_node, pointer_type);
     }
-    bool is_inline = node->data.for_expr.is_inline;
+    bool is_inline = ir_should_inline(irb) || node->data.for_expr.is_inline;
 
     BlockContext *child_scope = new_block_context(node, parent_scope);
     child_scope->parent_loop_node = node;
@@ -2219,7 +2298,7 @@ static IrInstruction *ir_gen_if_var_expr(IrBuilder *irb, AstNode *node) {
     IrBasicBlock *else_block = ir_build_basic_block(irb, "MaybeElse");
     IrBasicBlock *endif_block = ir_build_basic_block(irb, "MaybeEndIf");
 
-    bool is_inline = (node->block_context->fn_entry == nullptr);
+    bool is_inline = ir_should_inline(irb) || node->data.if_var_expr.is_inline;
     ir_build_cond_br(irb, node, is_nonnull_value, then_block, else_block, is_inline);
 
     ir_set_cursor_at_end(irb, then_block);
@@ -2322,7 +2401,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, AstNode *node) {
 
     size_t prong_count = node->data.switch_expr.prongs.length;
     ZigList<IrInstructionSwitchBrCase> cases = {0};
-    bool is_inline = node->data.switch_expr.is_inline || (node->block_context->fn_entry == nullptr);
+    bool is_inline = ir_should_inline(irb) || node->data.switch_expr.is_inline;
 
     ZigList<IrInstruction *> incoming_values = {0};
     ZigList<IrBasicBlock *> incoming_blocks = {0};
@@ -2495,7 +2574,7 @@ static IrInstruction *ir_gen_label(IrBuilder *irb, AstNode *node) {
         node->block_context->label_table.put(label_name, label);
     }
 
-    bool is_inline = (node->block_context->fn_entry == nullptr);
+    bool is_inline = ir_should_inline(irb);
     ir_build_br(irb, node, label_block, is_inline);
     ir_set_cursor_at_end(irb, label_block);
     return ir_build_const_void(irb, node);
@@ -2535,56 +2614,58 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, BlockContex
     node->block_context = block_context;
 
     switch (node->type) {
+        case NodeTypeStructValueField:
+            zig_unreachable();
         case NodeTypeBlock:
-            return ir_gen_block(irb, node);
+            return ir_lval_wrap(irb, ir_gen_block(irb, node), lval);
         case NodeTypeBinOpExpr:
-            return ir_gen_bin_op(irb, node);
+            return ir_lval_wrap(irb, ir_gen_bin_op(irb, node), lval);
         case NodeTypeNumberLiteral:
-            return ir_gen_num_lit(irb, node);
+            return ir_lval_wrap(irb, ir_gen_num_lit(irb, node), lval);
         case NodeTypeSymbol:
             return ir_gen_symbol(irb, node, lval);
         case NodeTypeFnCallExpr:
             return ir_lval_wrap(irb, ir_gen_fn_call(irb, node), lval);
         case NodeTypeIfBoolExpr:
-            return ir_gen_if_bool_expr(irb, node);
+            return ir_lval_wrap(irb, ir_gen_if_bool_expr(irb, node), lval);
         case NodeTypePrefixOpExpr:
             return ir_gen_prefix_op_expr(irb, node, lval);
         case NodeTypeContainerInitExpr:
-            return ir_gen_container_init_expr(irb, node);
+            return ir_lval_wrap(irb, ir_gen_container_init_expr(irb, node), lval);
         case NodeTypeVariableDeclaration:
-            return ir_gen_var_decl(irb, node);
+            return ir_lval_wrap(irb, ir_gen_var_decl(irb, node), lval);
         case NodeTypeWhileExpr:
-            return ir_gen_while_expr(irb, node);
+            return ir_lval_wrap(irb, ir_gen_while_expr(irb, node), lval);
         case NodeTypeForExpr:
-            return ir_gen_for_expr(irb, node);
+            return ir_lval_wrap(irb, ir_gen_for_expr(irb, node), lval);
         case NodeTypeArrayAccessExpr:
             return ir_gen_array_access(irb, node, lval);
         case NodeTypeReturnExpr:
-            return ir_gen_return(irb, node);
+            return ir_lval_wrap(irb, ir_gen_return(irb, node), lval);
         case NodeTypeFieldAccessExpr:
             return ir_gen_field_access(irb, node, lval);
         case NodeTypeThisLiteral:
-            return ir_gen_this_literal(irb, node);
+            return ir_lval_wrap(irb, ir_gen_this_literal(irb, node), lval);
         case NodeTypeBoolLiteral:
-            return ir_gen_bool_literal(irb, node);
+            return ir_lval_wrap(irb, ir_gen_bool_literal(irb, node), lval);
         case NodeTypeArrayType:
-            return ir_gen_array_type(irb, node);
+            return ir_lval_wrap(irb, ir_gen_array_type(irb, node), lval);
         case NodeTypeStringLiteral:
-            return ir_gen_string_literal(irb, node);
+            return ir_lval_wrap(irb, ir_gen_string_literal(irb, node), lval);
         case NodeTypeUndefinedLiteral:
-            return ir_gen_undefined_literal(irb, node);
+            return ir_lval_wrap(irb, ir_gen_undefined_literal(irb, node), lval);
         case NodeTypeAsmExpr:
-            return ir_gen_asm_expr(irb, node);
+            return ir_lval_wrap(irb, ir_gen_asm_expr(irb, node), lval);
         case NodeTypeNullLiteral:
-            return ir_gen_null_literal(irb, node);
+            return ir_lval_wrap(irb, ir_gen_null_literal(irb, node), lval);
         case NodeTypeIfVarExpr:
-            return ir_gen_if_var_expr(irb, node);
+            return ir_lval_wrap(irb, ir_gen_if_var_expr(irb, node), lval);
         case NodeTypeSwitchExpr:
-            return ir_gen_switch_expr(irb, node);
+            return ir_lval_wrap(irb, ir_gen_switch_expr(irb, node), lval);
         case NodeTypeLabel:
-            return ir_gen_label(irb, node);
+            return ir_lval_wrap(irb, ir_gen_label(irb, node), lval);
         case NodeTypeGoto:
-            return ir_gen_goto(irb, node);
+            return ir_lval_wrap(irb, ir_gen_goto(irb, node), lval);
         case NodeTypeTypeLiteral:
             return ir_lval_wrap(irb, ir_gen_type_literal(irb, node), lval);
         case NodeTypeUnwrapErrorExpr:
@@ -2604,7 +2685,6 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, BlockContex
         case NodeTypeUse:
         case NodeTypeContainerDecl:
         case NodeTypeStructField:
-        case NodeTypeStructValueField:
         case NodeTypeSwitchProng:
         case NodeTypeSwitchRange:
         case NodeTypeErrorValueDecl:
@@ -2642,7 +2722,7 @@ static bool ir_goto_pass2(IrBuilder *irb) {
         }
         label->used = true;
 
-        bool is_inline = goto_node->data.goto_expr.is_inline || (goto_node->block_context->fn_entry == nullptr);
+        bool is_inline = ir_should_inline(irb) || goto_node->data.goto_expr.is_inline;
         IrInstruction *new_instruction = ir_create_br(irb, goto_node, label->bb, is_inline);
         new_instruction->ref_count = old_instruction->ref_count;
         *slot = new_instruction;
@@ -2703,6 +2783,21 @@ IrInstruction *ir_gen_fn(CodeGen *codegn, FnTableEntry *fn_entry) {
     return ir_gen(codegn, body_node, scope, ir_executable);
 }
 
+static IrInstruction *ir_eval_fn(IrAnalyze *ira, IrInstruction *source_instruction,
+    size_t arg_count, IrInstruction **args)
+{
+    zig_panic("TODO ir_eval_fn");
+}
+
+static bool ir_emit_global_runtime_side_effect(IrAnalyze *ira, IrInstruction *source_instruction) {
+    if (ir_should_inline(&ira->new_irb)) {
+        add_node_error(ira->codegen, source_instruction->source_node,
+                buf_sprintf("unable to evaluate constant expression"));
+        return false;
+    }
+    return true;
+}
+
 static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruction, TypeTableEntry *other_type) {
     TypeTableEntry *other_type_underlying = get_underlying_type(other_type);
 
@@ -2918,20 +3013,15 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
 }
 
 static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value,
-        IrInstruction *dest_type, CastOp cast_op, bool need_alloca)
+        TypeTableEntry *wanted_type, CastOp cast_op, bool need_alloca)
 {
-    assert(dest_type->type_entry->id == TypeTableEntryIdMetaType);
-    assert(dest_type->static_value.special != ConstValSpecialRuntime);
-    TypeTableEntry *wanted_type = dest_type->static_value.data.x_type;
-
     if (value->static_value.special != ConstValSpecialRuntime) {
-        IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->source_node, wanted_type);
+        IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->source_node, wanted_type, false);
         eval_const_expr_implicit_cast(cast_op, &value->static_value, value->type_entry,
                 &result->static_value, wanted_type);
         return result;
     } else {
-        IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->source_node,
-                dest_type, value, cast_op);
+        IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->source_node, wanted_type, value, cast_op);
         result->type_entry = wanted_type;
         if (need_alloca && source_instr->source_node->block_context->fn_entry) {
             source_instr->source_node->block_context->fn_entry->alloca_list.append(result);
@@ -3126,12 +3216,8 @@ static FnTableEntry *ir_resolve_fn(IrAnalyze *ira, IrInstruction *fn_value) {
 }
 
 static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr,
-    IrInstruction *dest_type, IrInstruction *value)
+    TypeTableEntry *wanted_type, IrInstruction *value)
 {
-    assert(dest_type->type_entry->id == TypeTableEntryIdMetaType);
-    assert(dest_type->static_value.special != ConstValSpecialRuntime);
-
-    TypeTableEntry *wanted_type = dest_type->static_value.data.x_type;
     TypeTableEntry *actual_type = value->type_entry;
     TypeTableEntry *wanted_type_canon = get_underlying_type(wanted_type);
     TypeTableEntry *actual_type_canon = get_underlying_type(actual_type);
@@ -3147,21 +3233,21 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
 
     // explicit match or non-const to const
     if (types_match_const_cast_only(wanted_type, actual_type)) {
-        return ir_resolve_cast(ira, source_instr, value, dest_type, CastOpNoop, false);
+        return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false);
     }
 
     // explicit cast from bool to int
     if (wanted_type_canon->id == TypeTableEntryIdInt &&
         actual_type_canon->id == TypeTableEntryIdBool)
     {
-        return ir_resolve_cast(ira, source_instr, value, dest_type, CastOpBoolToInt, false);
+        return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpBoolToInt, false);
     }
 
     // explicit cast from pointer to isize or usize
     if ((wanted_type_canon == isize_type || wanted_type_canon == usize_type) &&
         type_is_codegen_pointer(actual_type_canon))
     {
-        return ir_resolve_cast(ira, source_instr, value, dest_type, CastOpPtrToInt, false);
+        return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpPtrToInt, false);
     }
 
 
@@ -3169,7 +3255,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
     if (wanted_type_canon->id == TypeTableEntryIdPointer &&
         (actual_type_canon == isize_type || actual_type_canon == usize_type))
     {
-        return ir_resolve_cast(ira, source_instr, value, dest_type, CastOpIntToPtr, false);
+        return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpIntToPtr, false);
     }
 
     // explicit widening or shortening cast
@@ -3178,21 +3264,21 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
         (wanted_type_canon->id == TypeTableEntryIdFloat &&
         actual_type_canon->id == TypeTableEntryIdFloat))
     {
-        return ir_resolve_cast(ira, source_instr, value, dest_type, CastOpWidenOrShorten, false);
+        return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpWidenOrShorten, false);
     }
 
     // explicit cast from int to float
     if (wanted_type_canon->id == TypeTableEntryIdFloat &&
         actual_type_canon->id == TypeTableEntryIdInt)
     {
-        return ir_resolve_cast(ira, source_instr, value, dest_type, CastOpIntToFloat, false);
+        return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpIntToFloat, false);
     }
 
     // explicit cast from float to int
     if (wanted_type_canon->id == TypeTableEntryIdInt &&
         actual_type_canon->id == TypeTableEntryIdFloat)
     {
-        return ir_resolve_cast(ira, source_instr, value, dest_type, CastOpFloatToInt, false);
+        return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpFloatToInt, false);
     }
 
     // explicit cast from array to slice
@@ -3202,7 +3288,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
             wanted_type->data.structure.fields[0].type_entry->data.pointer.child_type,
             actual_type->data.array.child_type))
     {
-        return ir_resolve_cast(ira, source_instr, value, dest_type, CastOpToUnknownSizeArray, true);
+        return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpToUnknownSizeArray, true);
     }
 
     // explicit cast from []T to []u8 or []u8 to []T
@@ -3212,8 +3298,9 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
         (wanted_type->data.structure.fields[0].type_entry->data.pointer.is_const ||
          !actual_type->data.structure.fields[0].type_entry->data.pointer.is_const))
     {
-        mark_impure_fn(ira->codegen, source_instr->source_node->block_context, source_instr->source_node);
-        return ir_resolve_cast(ira, source_instr, value, dest_type, CastOpResizeSlice, true);
+        if (!ir_emit_global_runtime_side_effect(ira, source_instr))
+            return ira->codegen->invalid_instruction;
+        return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpResizeSlice, true);
     }
 
     // explicit cast from [N]u8 to []T
@@ -3221,11 +3308,12 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
         actual_type->id == TypeTableEntryIdArray &&
         is_u8(actual_type->data.array.child_type))
     {
-        mark_impure_fn(ira->codegen, source_instr->source_node->block_context, source_instr->source_node);
+        if (!ir_emit_global_runtime_side_effect(ira, source_instr))
+            return ira->codegen->invalid_instruction;
         uint64_t child_type_size = type_size(ira->codegen,
                 wanted_type->data.structure.fields[0].type_entry->data.pointer.child_type);
         if (actual_type->data.array.len % child_type_size == 0) {
-            return ir_resolve_cast(ira, source_instr, value, dest_type, CastOpBytesToSlice, true);
+            return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpBytesToSlice, true);
         } else {
             add_node_error(ira->codegen, source_instr->source_node,
                     buf_sprintf("unable to convert %s to %s: size mismatch",
@@ -3238,7 +3326,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
     if ((actual_type->id == TypeTableEntryIdPointer || actual_type->id == TypeTableEntryIdFn) &&
         (wanted_type->id == TypeTableEntryIdPointer || wanted_type->id == TypeTableEntryIdFn))
     {
-        return ir_resolve_cast(ira, source_instr, value, dest_type, CastOpPointerReinterpret, false);
+        return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpPointerReinterpret, false);
     }
 
     // explicit cast from maybe pointer to another maybe pointer
@@ -3249,13 +3337,13 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
         (wanted_type->data.maybe.child_type->id == TypeTableEntryIdPointer ||
             wanted_type->data.maybe.child_type->id == TypeTableEntryIdFn))
     {
-        return ir_resolve_cast(ira, source_instr, value, dest_type, CastOpPointerReinterpret, false);
+        return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpPointerReinterpret, false);
     }
 
     // explicit cast from child type of maybe type to maybe type
     if (wanted_type->id == TypeTableEntryIdMaybe) {
         if (types_match_const_cast_only(wanted_type->data.maybe.child_type, actual_type)) {
-            IrInstruction *cast_instruction = ir_resolve_cast(ira, source_instr, value, dest_type,
+            IrInstruction *cast_instruction = ir_resolve_cast(ira, source_instr, value, wanted_type,
                     CastOpMaybeWrap, true);
             cast_instruction->return_knowledge = ReturnKnowledgeKnownNonNull;
             return cast_instruction;
@@ -3263,7 +3351,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
                    actual_type->id == TypeTableEntryIdNumLitFloat)
         {
             if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.maybe.child_type)) {
-                IrInstruction *cast_instruction = ir_resolve_cast(ira, source_instr, value, dest_type,
+                IrInstruction *cast_instruction = ir_resolve_cast(ira, source_instr, value, wanted_type,
                         CastOpMaybeWrap, true);
                 cast_instruction->return_knowledge = ReturnKnowledgeKnownNonNull;
                 return cast_instruction;
@@ -3277,7 +3365,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
     if (wanted_type->id == TypeTableEntryIdMaybe &&
         actual_type->id == TypeTableEntryIdNullLit)
     {
-        IrInstruction *cast_instruction = ir_resolve_cast(ira, source_instr, value, dest_type,
+        IrInstruction *cast_instruction = ir_resolve_cast(ira, source_instr, value, wanted_type,
                 CastOpNullToMaybe, true);
         cast_instruction->return_knowledge = ReturnKnowledgeKnownNull;
         return cast_instruction;
@@ -3286,7 +3374,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
     // explicit cast from child type of error type to error type
     if (wanted_type->id == TypeTableEntryIdErrorUnion) {
         if (types_match_const_cast_only(wanted_type->data.error.child_type, actual_type)) {
-            IrInstruction *cast_instruction = ir_resolve_cast(ira, source_instr, value, dest_type,
+            IrInstruction *cast_instruction = ir_resolve_cast(ira, source_instr, value, wanted_type,
                     CastOpErrorWrap, true);
             cast_instruction->return_knowledge = ReturnKnowledgeKnownNonError;
             return cast_instruction;
@@ -3294,7 +3382,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
                    actual_type->id == TypeTableEntryIdNumLitFloat)
         {
             if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.error.child_type)) {
-                IrInstruction *cast_instruction = ir_resolve_cast(ira, source_instr, value, dest_type,
+                IrInstruction *cast_instruction = ir_resolve_cast(ira, source_instr, value, wanted_type,
                         CastOpErrorWrap, true);
                 cast_instruction->return_knowledge = ReturnKnowledgeKnownNonError;
                 return cast_instruction;
@@ -3308,7 +3396,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
     if (wanted_type->id == TypeTableEntryIdErrorUnion &&
         actual_type->id == TypeTableEntryIdPureError)
     {
-        IrInstruction *cast_instruction = ir_resolve_cast(ira, source_instr, value, dest_type,
+        IrInstruction *cast_instruction = ir_resolve_cast(ira, source_instr, value, wanted_type,
                 CastOpPureErrorWrap, false);
         cast_instruction->return_knowledge = ReturnKnowledgeKnownError;
         return cast_instruction;
@@ -3333,7 +3421,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
             } else {
                 zig_unreachable();
             }
-            return ir_resolve_cast(ira, source_instr, value, dest_type, op, false);
+            return ir_resolve_cast(ira, source_instr, value, wanted_type, op, false);
         } else {
             return ira->codegen->invalid_instruction;
         }
@@ -3351,7 +3439,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
         if (bignum_fits_in_bits(&bn, wanted_type->data.integral.bit_count,
                     wanted_type->data.integral.is_signed))
         {
-            return ir_resolve_cast(ira, source_instr, value, dest_type, CastOpErrToInt, false);
+            return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpErrToInt, false);
         } else {
             add_node_error(ira->codegen, source_instr->source_node,
                     buf_sprintf("too many error values to fit in '%s'", buf_ptr(&wanted_type->name)));
@@ -3364,7 +3452,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
         wanted_type->id == TypeTableEntryIdEnum &&
         wanted_type->data.enumeration.gen_field_count == 0)
     {
-        return ir_resolve_cast(ira, source_instr, value, dest_type, CastOpIntToEnum, false);
+        return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpIntToEnum, false);
     }
 
     // explicit cast from enum type with no payload to integer
@@ -3372,12 +3460,12 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
         actual_type->id == TypeTableEntryIdEnum &&
         actual_type->data.enumeration.gen_field_count == 0)
     {
-        return ir_resolve_cast(ira, source_instr, value, dest_type, CastOpEnumToInt, false);
+        return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpEnumToInt, false);
     }
 
     // explicit cast from undefined to anything
     if (actual_type->id == TypeTableEntryIdUndefLit) {
-        return ir_resolve_cast(ira, source_instr, value, dest_type, CastOpNoop, false);
+        return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false);
     }
 
     add_node_error(ira->codegen, source_instr->source_node,
@@ -3410,11 +3498,7 @@ static IrInstruction *ir_get_casted_value(IrAnalyze *ira, IrInstruction *value,
             return ira->codegen->invalid_instruction;
 
         case ImplicitCastMatchResultYes:
-            {
-                IrInstruction *dest_type = ir_create_const_type(&ira->new_irb, value->source_node, expected_type);
-                IrInstruction *cast_instruction = ir_analyze_cast(ira, value, dest_type, value);
-                return cast_instruction;
-            }
+            return ir_analyze_cast(ira, value, expected_type, value);
         case ImplicitCastMatchResultReportedError:
             return ira->codegen->invalid_instruction;
     }
@@ -3422,6 +3506,58 @@ static IrInstruction *ir_get_casted_value(IrAnalyze *ira, IrInstruction *value,
     zig_unreachable();
 }
 
+static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *ptr) {
+    TypeTableEntry *type_entry = ptr->type_entry;
+    if (type_entry->id == TypeTableEntryIdInvalid) {
+        return ira->codegen->invalid_instruction;
+    } else if (type_entry->id == TypeTableEntryIdPointer) {
+        TypeTableEntry *child_type = type_entry->data.pointer.child_type;
+        if (ptr->static_value.special != ConstValSpecialRuntime) {
+            ConstExprValue *pointee = const_ptr_pointee(&ptr->static_value);
+            if (pointee->special != ConstValSpecialRuntime) {
+                IrInstruction *result = ir_create_const(&ira->new_irb, source_instruction->source_node,
+                    child_type, pointee->depends_on_compile_var);
+                result->static_value = *pointee;
+                return result;
+            }
+        }
+        IrInstruction *load_ptr_instruction = ir_build_load_ptr(&ira->new_irb, source_instruction->source_node, ptr);
+        load_ptr_instruction->type_entry = child_type;
+        return load_ptr_instruction;
+    } else {
+        add_node_error(ira->codegen, source_instruction->source_node,
+            buf_sprintf("attempt to dereference non pointer type '%s'",
+                buf_ptr(&type_entry->name)));
+        return ira->codegen->invalid_instruction;
+    }
+}
+
+static TypeTableEntry *ir_analyze_ref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *value) {
+    if (value->type_entry->id == TypeTableEntryIdInvalid)
+        return ira->codegen->builtin_types.entry_invalid;
+
+    bool is_inline = ir_should_inline(&ira->new_irb);
+    if (is_inline || value->static_value.special != ConstValSpecialRuntime) {
+        ConstExprValue *val = ir_resolve_const(ira, value);
+        if (!val)
+            return ira->codegen->builtin_types.entry_invalid;
+        return ir_analyze_const_ptr(ira, source_instruction, val, value->type_entry, false);
+    }
+
+    TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, value->type_entry, true);
+    if (handle_is_ptr(value->type_entry)) {
+        // this instruction is a noop - codegen can pass the pointer we already have as the result
+        ir_link_new_instruction(value, source_instruction);
+        return ptr_type;
+    } else {
+        FnTableEntry *fn_entry = source_instruction->source_node->block_context->fn_entry;
+        IrInstruction *new_instruction = ir_build_ref_from(&ira->new_irb, source_instruction, value);
+        fn_entry->alloca_list.append(new_instruction);
+        return ptr_type;
+    }
+}
+
+
 static bool ir_resolve_usize(IrAnalyze *ira, IrInstruction *value, uint64_t *out) {
     if (value->type_entry->id == TypeTableEntryIdInvalid)
         return false;
@@ -3580,6 +3716,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
         case TypeTableEntryIdNamespace:
         case TypeTableEntryIdBlock:
         case TypeTableEntryIdGenericFn:
+        case TypeTableEntryIdBoundFn:
             if (!is_equality_cmp) {
                 add_node_error(ira->codegen, source_node,
                     buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name)));
@@ -3944,6 +4081,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc
         case TypeTableEntryIdUnion:
         case TypeTableEntryIdFn:
         case TypeTableEntryIdGenericFn:
+        case TypeTableEntryIdBoundFn:
             // OK
             break;
     }
@@ -3966,13 +4104,121 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc
     return ira->codegen->builtin_types.entry_void;
 }
 
+static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call_instruction,
+    FnTableEntry *fn_entry, TypeTableEntry *fn_type, IrInstruction *fn_ref,
+    IrInstruction *first_arg_ptr, bool is_inline)
+{
+    FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
+    size_t first_arg_1_or_0 = first_arg_ptr ? 1 : 0;
+    size_t src_param_count = fn_type_id->param_count;
+    size_t call_param_count = call_instruction->arg_count + first_arg_1_or_0;
+    AstNode *source_node = call_instruction->base.source_node;
+
+    AstNode *fn_proto_node = fn_entry ? fn_entry->proto_node : nullptr;;
+
+    if (fn_type_id->is_var_args) {
+        if (call_param_count < src_param_count) {
+            ErrorMsg *msg = add_node_error(ira->codegen, source_node,
+                buf_sprintf("expected at least %zu arguments, found %zu", src_param_count, call_param_count));
+            if (fn_proto_node) {
+                add_error_note(ira->codegen, msg, fn_proto_node,
+                    buf_sprintf("declared here"));
+            }
+            return ira->codegen->builtin_types.entry_invalid;
+        }
+    } else if (src_param_count != call_param_count) {
+        ErrorMsg *msg = add_node_error(ira->codegen, source_node,
+            buf_sprintf("expected %zu arguments, found %zu", src_param_count, call_param_count));
+        if (fn_proto_node) {
+            add_error_note(ira->codegen, msg, fn_proto_node,
+                buf_sprintf("declared here"));
+        }
+        return ira->codegen->builtin_types.entry_invalid;
+    }
+
+    IrInstruction **casted_args = allocate<IrInstruction *>(call_param_count);
+    size_t next_arg_index = 0;
+    if (first_arg_ptr) {
+        IrInstruction *first_arg = ir_get_deref(ira, first_arg_ptr, first_arg_ptr);
+        if (first_arg->type_entry->id == TypeTableEntryIdInvalid)
+            return ira->codegen->builtin_types.entry_invalid;
+
+        TypeTableEntry *param_type = fn_type_id->param_info[next_arg_index].type;
+        if (param_type->id == TypeTableEntryIdInvalid)
+            return ira->codegen->builtin_types.entry_invalid;
+
+        IrInstruction *casted_arg = ir_get_casted_value(ira, first_arg, param_type);
+        if (casted_arg->type_entry->id == TypeTableEntryIdInvalid)
+            return ira->codegen->builtin_types.entry_invalid;
+
+        if (is_inline && !ir_resolve_const(ira, casted_arg))
+            return ira->codegen->builtin_types.entry_invalid;
+
+        casted_args[next_arg_index] = casted_arg;
+        next_arg_index += 1;
+    }
+    for (size_t call_i = 0; call_i < call_instruction->arg_count; call_i += 1) {
+        IrInstruction *old_arg = call_instruction->args[call_i]->other;
+        if (old_arg->type_entry->id == TypeTableEntryIdInvalid)
+            return ira->codegen->builtin_types.entry_invalid;
+        IrInstruction *casted_arg;
+        if (next_arg_index < src_param_count) {
+            TypeTableEntry *param_type = fn_type_id->param_info[next_arg_index].type;
+            if (param_type->id == TypeTableEntryIdInvalid)
+                return ira->codegen->builtin_types.entry_invalid;
+            casted_arg = ir_get_casted_value(ira, old_arg, param_type);
+            if (casted_arg->type_entry->id == TypeTableEntryIdInvalid)
+                return ira->codegen->builtin_types.entry_invalid;
+        } else {
+            casted_arg = old_arg;
+        }
+
+        if (is_inline && !ir_resolve_const(ira, casted_arg))
+            return ira->codegen->builtin_types.entry_invalid;
+
+        casted_args[next_arg_index] = casted_arg;
+        next_arg_index += 1;
+    }
+
+    assert(next_arg_index == call_param_count);
+
+    TypeTableEntry *return_type = fn_type_id->return_type;
+    if (return_type->id == TypeTableEntryIdInvalid)
+        return ira->codegen->builtin_types.entry_invalid;
+
+    if (is_inline) {
+        IrInstruction *result = ir_eval_fn(ira, &call_instruction->base, call_param_count, casted_args);
+        if (result->type_entry->id == TypeTableEntryIdInvalid)
+            return ira->codegen->builtin_types.entry_invalid;
+
+        ConstExprValue *out_val = ir_build_const_from(ira, &call_instruction->base,
+                result->static_value.depends_on_compile_var);
+        *out_val = result->static_value;
+        return ir_finish_anal(ira, return_type);
+    }
+
+    IrInstruction *new_call_instruction = ir_build_call_from(&ira->new_irb, &call_instruction->base,
+            fn_entry, fn_ref, call_param_count, casted_args);
+
+    if (type_has_bits(return_type) && handle_is_ptr(return_type))
+        call_instruction->base.source_node->block_context->fn_entry->alloca_list.append(new_call_instruction);
+
+    return ir_finish_anal(ira, return_type);
+}
+
 static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstructionCall *call_instruction) {
-    IrInstruction *fn_ref = call_instruction->fn->other;
+    IrInstruction *fn_ref = call_instruction->fn_ref->other;
     if (fn_ref->type_entry->id == TypeTableEntryIdInvalid)
         return ira->codegen->builtin_types.entry_invalid;
 
-    if (fn_ref->static_value.special != ConstValSpecialRuntime) {
+    bool is_inline = call_instruction->is_inline || ir_should_inline(&ira->new_irb);
+
+    if (is_inline || fn_ref->static_value.special != ConstValSpecialRuntime) {
         if (fn_ref->type_entry->id == TypeTableEntryIdMetaType) {
+            TypeTableEntry *dest_type = ir_resolve_type(ira, fn_ref);
+            if (!dest_type)
+                return ira->codegen->builtin_types.entry_invalid;
+
             size_t actual_param_count = call_instruction->arg_count;
 
             if (actual_param_count != 1) {
@@ -3982,38 +4228,39 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction
             }
 
             IrInstruction *arg = call_instruction->args[0]->other;
-            IrInstruction *cast_instruction = ir_analyze_cast(ira, &call_instruction->base, fn_ref, arg);
-            if (cast_instruction == ira->codegen->invalid_instruction)
+
+            IrInstruction *cast_instruction = ir_analyze_cast(ira, &call_instruction->base, dest_type, arg);
+            if (cast_instruction->type_entry->id == TypeTableEntryIdInvalid)
                 return ira->codegen->builtin_types.entry_invalid;
 
             ir_link_new_instruction(cast_instruction, &call_instruction->base);
             return ir_finish_anal(ira, cast_instruction->type_entry);
         } else if (fn_ref->type_entry->id == TypeTableEntryIdFn) {
-            // TODO fully port over the fn call analyze code to IR
-            FnTableEntry *fn_table_entry = fn_ref->static_value.data.x_fn;
-            TypeTableEntry *fn_type = fn_table_entry->type_entry;
-
-            IrInstruction **casted_args = allocate<IrInstruction *>(call_instruction->arg_count);
-            for (size_t i = 0; i < call_instruction->arg_count; i += 1) {
-                TypeTableEntry *param_type = fn_type->data.fn.fn_type_id.param_info[i].type;
-                IrInstruction *old_arg = call_instruction->args[i]->other;
-                if (old_arg->type_entry->id == TypeTableEntryIdInvalid)
-                    return ira->codegen->builtin_types.entry_invalid;
-                casted_args[i] = ir_get_casted_value(ira, old_arg, param_type);
-            }
-
-            ir_build_call_from(&ira->new_irb, &call_instruction->base,
-                    fn_ref, call_instruction->arg_count, casted_args);
-
-            return ir_finish_anal(ira, fn_type->data.fn.fn_type_id.return_type);
+            FnTableEntry *fn_table_entry = ir_resolve_fn(ira, fn_ref);
+            return ir_analyze_fn_call(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry,
+                fn_ref, nullptr, is_inline);
+        } else if (fn_ref->type_entry->id == TypeTableEntryIdBoundFn) {
+            assert(fn_ref->static_value.special == ConstValSpecialStatic);
+            FnTableEntry *fn_table_entry = fn_ref->static_value.data.x_bound_fn.fn;
+            IrInstruction *first_arg_ptr = fn_ref->static_value.data.x_bound_fn.first_arg;
+            return ir_analyze_fn_call(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry,
+                nullptr, first_arg_ptr, is_inline);
+        } else if (fn_ref->type_entry->id == TypeTableEntryIdGenericFn) {
+            zig_panic("TODO generic fn call");
         } else {
-            zig_panic("TODO analyze more fn call types");
+            add_node_error(ira->codegen, fn_ref->source_node,
+                buf_sprintf("type '%s' not a function", buf_ptr(&fn_ref->type_entry->name)));
+            return ira->codegen->builtin_types.entry_invalid;
         }
-    } else {
-        //ir_build_call_from(&ira->new_irb, &call_instruction->base,
-        //        call_instruction->fn, call_instruction->arg_count, call_instruction->args);
+    }
 
-        zig_panic("TODO analyze fn call");
+    if (fn_ref->type_entry->id == TypeTableEntryIdFn) {
+        return ir_analyze_fn_call(ira, call_instruction, nullptr, fn_ref->type_entry,
+            fn_ref, nullptr, false);
+    } else {
+        add_node_error(ira->codegen, fn_ref->source_node,
+            buf_sprintf("type '%s' not a function", buf_ptr(&fn_ref->type_entry->name)));
+        return ira->codegen->builtin_types.entry_invalid;
     }
 }
 
@@ -4071,6 +4318,7 @@ static TypeTableEntry *ir_analyze_unary_prefix_op_err(IrAnalyze *ira, IrInstruct
         case TypeTableEntryIdUnion:
         case TypeTableEntryIdFn:
         case TypeTableEntryIdGenericFn:
+        case TypeTableEntryIdBoundFn:
             {
                 ConstExprValue *out_val = ir_build_const_from(ira, &un_op_instruction->base,
                         value->static_value.depends_on_compile_var);
@@ -4119,6 +4367,7 @@ static TypeTableEntry *ir_analyze_unary_address_of(IrAnalyze *ira, IrInstruction
         case TypeTableEntryIdUnreachable:
         case TypeTableEntryIdVar:
         case TypeTableEntryIdGenericFn:
+        case TypeTableEntryIdBoundFn:
             add_node_error(ira->codegen, un_op_instruction->base.source_node,
                     buf_sprintf("unable to get address of type '%s'", buf_ptr(&target_type->name)));
             // TODO if type decl, add note pointing to type decl declaration
@@ -4217,6 +4466,7 @@ static TypeTableEntry *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op
         case TypeTableEntryIdNamespace:
         case TypeTableEntryIdBlock:
         case TypeTableEntryIdGenericFn:
+        case TypeTableEntryIdBoundFn:
             {
                 ConstExprValue *out_val = ir_build_const_from(ira, &un_op_instruction->base,
                         value->static_value.depends_on_compile_var);
@@ -4626,7 +4876,7 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc
 
 static TypeTableEntry *ir_analyze_container_member_access_inner(IrAnalyze *ira,
     TypeTableEntry *bare_struct_type, Buf *field_name, IrInstructionFieldPtr *field_ptr_instruction,
-    TypeTableEntry *container_type)
+    IrInstruction *container_ptr, TypeTableEntry *container_type)
 {
     if (!is_slice(bare_struct_type)) {
         BlockContext *container_block_context = get_container_block_context(bare_struct_type);
@@ -4634,7 +4884,15 @@ static TypeTableEntry *ir_analyze_container_member_access_inner(IrAnalyze *ira,
         auto entry = container_block_context->decl_table.maybe_get(field_name);
         AstNode *fn_decl_node = entry ? entry->value : nullptr;
         if (fn_decl_node && fn_decl_node->type == NodeTypeFnProto) {
-            zig_panic("TODO member function call");
+            resolve_top_level_decl(ira->codegen, fn_decl_node, false);
+            TopLevelDecl *tld = get_as_top_level_decl(fn_decl_node);
+            if (tld->resolution == TldResolutionInvalid)
+                return ira->codegen->builtin_types.entry_invalid;
+            FnTableEntry *fn_entry = fn_decl_node->data.fn_proto.fn_table_entry;
+            bool depends_on_compile_var = container_ptr->static_value.depends_on_compile_var;
+            IrInstruction *bound_fn_value = ir_build_const_bound_fn(&ira->new_irb,
+                field_ptr_instruction->base.source_node, fn_entry, container_ptr, depends_on_compile_var);
+            return ir_analyze_ref(ira, &field_ptr_instruction->base, bound_fn_value);
         }
     }
     add_node_error(ira->codegen, field_ptr_instruction->base.source_node,
@@ -4643,14 +4901,12 @@ static TypeTableEntry *ir_analyze_container_member_access_inner(IrAnalyze *ira,
 }
 
 
-static TypeTableEntry *ir_analyze_container_member_access(IrAnalyze *ira, Buf *field_name,
-    IrInstructionFieldPtr *field_ptr_instruction, TypeTableEntry *container_type)
+static TypeTableEntry *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name,
+    IrInstructionFieldPtr *field_ptr_instruction, IrInstruction *container_ptr, TypeTableEntry *container_type)
 {
-    IrInstruction *container_ptr = field_ptr_instruction->container_ptr->other;
     TypeTableEntry *bare_type = container_ref_type(container_type);
-    if (!type_is_complete(bare_type)) {
+    if (!type_is_complete(bare_type))
         resolve_container_type(ira->codegen, bare_type);
-    }
 
     if (bare_type->id == TypeTableEntryIdStruct) {
         TypeStructField *field = find_struct_type_field(bare_type, field_name);
@@ -4659,10 +4915,17 @@ static TypeTableEntry *ir_analyze_container_member_access(IrAnalyze *ira, Buf *f
             return get_pointer_to_type(ira->codegen, field->type_entry, false);
         } else {
             return ir_analyze_container_member_access_inner(ira, bare_type, field_name,
-                field_ptr_instruction, container_type);
+                field_ptr_instruction, container_ptr, container_type);
         }
     } else if (bare_type->id == TypeTableEntryIdEnum) {
-        zig_panic("TODO enum field ptr");
+        TypeEnumField *field = find_enum_type_field(bare_type, field_name);
+        if (field) {
+            ir_build_enum_field_ptr_from(&ira->new_irb, &field_ptr_instruction->base, container_ptr, field);
+            return get_pointer_to_type(ira->codegen, field->type_entry, false);
+        } else {
+            return ir_analyze_container_member_access_inner(ira, bare_type, field_name,
+                field_ptr_instruction, container_ptr, container_type);
+        }
     } else if (bare_type->id == TypeTableEntryIdUnion) {
         zig_panic("TODO");
     } else {
@@ -4733,7 +4996,7 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
     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);
+        return ir_analyze_container_field_ptr(ira, field_name, field_ptr_instruction, container_ptr, container_type);
     } else if (container_type->id == TypeTableEntryIdArray) {
         if (buf_eql_str(field_name, "len")) {
             ConstExprValue *len_val = allocate<ConstExprValue>(1);
@@ -4749,24 +5012,38 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
             return ira->codegen->builtin_types.entry_invalid;
         }
     } else if (container_type->id == TypeTableEntryIdMetaType) {
-        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;
-        //}
+        ConstExprValue *container_ptr_val = ir_resolve_const(ira, container_ptr);
+        if (!container_ptr_val)
+            return ira->codegen->builtin_types.entry_invalid;
+        ConstExprValue *child_val = const_ptr_pointee(container_ptr_val);
+        TypeTableEntry *child_type = child_val->data.x_type;
+
+        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) {
+            BlockContext *container_block_context = get_container_block_context(child_type);
+            auto entry = container_block_context->decl_table.maybe_get(field_name);
+            AstNode *decl_node = entry ? entry->value : nullptr;
+            if (decl_node) {
+                bool depends_on_compile_var = container_ptr->static_value.depends_on_compile_var;
+                return ir_analyze_decl_ref(ira, &field_ptr_instruction->base, decl_node, depends_on_compile_var);
+            } else {
+                add_node_error(ira->codegen, source_node,
+                    buf_sprintf("container '%s' has no member called '%s'",
+                        buf_ptr(&child_type->name), buf_ptr(field_name)));
+                return ira->codegen->builtin_types.entry_invalid;
+            }
+        } 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)
@@ -4817,28 +5094,10 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
 
 static TypeTableEntry *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstructionLoadPtr *load_ptr_instruction) {
     IrInstruction *ptr = load_ptr_instruction->ptr->other;
-    TypeTableEntry *type_entry = ptr->type_entry;
-    if (type_entry->id == TypeTableEntryIdInvalid) {
-        return type_entry;
-    } else if (type_entry->id == TypeTableEntryIdPointer) {
-        TypeTableEntry *child_type = type_entry->data.pointer.child_type;
-        if (ptr->static_value.special != ConstValSpecialRuntime) {
-            ConstExprValue *pointee = const_ptr_pointee(&ptr->static_value);
-            if (pointee->special != ConstValSpecialRuntime) {
-                ConstExprValue *out_val = ir_build_const_from(ira, &load_ptr_instruction->base,
-                        pointee->depends_on_compile_var);
-                *out_val = *pointee;
-                return child_type;
-            }
-        }
-        ir_build_load_ptr_from(&ira->new_irb, &load_ptr_instruction->base, ptr);
-        return child_type;
-    } else {
-        add_node_error(ira->codegen, load_ptr_instruction->base.source_node,
-            buf_sprintf("attempt to dereference non pointer type '%s'",
-                buf_ptr(&type_entry->name)));
-        return ira->codegen->builtin_types.entry_invalid;
-    }
+    IrInstruction *result = ir_get_deref(ira, &load_ptr_instruction->base, ptr);
+    ir_link_new_instruction(result, &load_ptr_instruction->base);
+    assert(result->type_entry);
+    return result->type_entry;
 }
 
 static TypeTableEntry *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstructionStorePtr *store_ptr_instruction) {
@@ -4911,6 +5170,7 @@ static TypeTableEntry *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructi
         case TypeTableEntryIdNamespace:
         case TypeTableEntryIdBlock:
         case TypeTableEntryIdGenericFn:
+        case TypeTableEntryIdBoundFn:
         case TypeTableEntryIdMetaType:
         case TypeTableEntryIdVoid:
         case TypeTableEntryIdBool:
@@ -5148,6 +5408,7 @@ static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira,
         case TypeTableEntryIdFn:
         case TypeTableEntryIdNamespace:
         case TypeTableEntryIdGenericFn:
+        case TypeTableEntryIdBoundFn:
             {
                 TypeTableEntry *result_type = get_slice_type(ira->codegen, resolved_child_type, is_const);
                 ConstExprValue *out_val = ir_build_const_from(ira, &slice_type_instruction->base,
@@ -5161,8 +5422,9 @@ static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira,
 
 static TypeTableEntry *ir_analyze_instruction_asm(IrAnalyze *ira, IrInstructionAsm *asm_instruction) {
     assert(asm_instruction->base.source_node->type == NodeTypeAsmExpr);
-    mark_impure_fn(ira->codegen, asm_instruction->base.source_node->block_context,
-            asm_instruction->base.source_node);
+
+    if (!ir_emit_global_runtime_side_effect(ira, &asm_instruction->base))
+        return ira->codegen->builtin_types.entry_invalid;
 
     // TODO validate the output types and variable types
 
@@ -5236,6 +5498,7 @@ static TypeTableEntry *ir_analyze_instruction_array_type(IrAnalyze *ira,
         case TypeTableEntryIdFn:
         case TypeTableEntryIdNamespace:
         case TypeTableEntryIdGenericFn:
+        case TypeTableEntryIdBoundFn:
             {
                 TypeTableEntry *result_type = get_array_type(ira->codegen, child_type, size);
                 bool depends_on_compile_var = child_type_value->static_value.depends_on_compile_var ||
@@ -5306,6 +5569,7 @@ static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira,
         case TypeTableEntryIdNumLitFloat:
         case TypeTableEntryIdNumLitInt:
         case TypeTableEntryIdGenericFn:
+        case TypeTableEntryIdBoundFn:
         case TypeTableEntryIdMetaType:
         case TypeTableEntryIdFn:
         case TypeTableEntryIdNamespace:
@@ -5469,7 +5733,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_br(IrAnalyze *ira,
         return ir_unreach_error(ira);
 
     size_t case_count = switch_br_instruction->case_count;
-    bool is_inline = switch_br_instruction->is_inline;
+    bool is_inline = ir_should_inline(&ira->new_irb) || switch_br_instruction->is_inline;
 
     if (is_inline || target_value->static_value.special != ConstValSpecialRuntime) {
         ConstExprValue *target_val = ir_resolve_const(ira, target_value);
@@ -5599,6 +5863,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,
         case TypeTableEntryIdUnion:
         case TypeTableEntryIdBlock:
         case TypeTableEntryIdGenericFn:
+        case TypeTableEntryIdBoundFn:
             add_node_error(ira->codegen, switch_target_instruction->base.source_node,
                 buf_sprintf("invalid switch target type '%s'", buf_ptr(&target_type->name)));
             // TODO if this is a typedecl, add error note showing the declaration of the type decl
@@ -5741,51 +6006,222 @@ static TypeTableEntry *ir_analyze_instruction_array_len(IrAnalyze *ira,
 
 static TypeTableEntry *ir_analyze_instruction_ref(IrAnalyze *ira, IrInstructionRef *ref_instruction) {
     IrInstruction *value = ref_instruction->value->other;
-    if (value->type_entry->id == TypeTableEntryIdInvalid)
-        return ira->codegen->builtin_types.entry_invalid;
+    return ir_analyze_ref(ira, &ref_instruction->base, value);
+}
 
-    FnTableEntry *fn_entry = ref_instruction->base.source_node->block_context->fn_entry;
-    if (!fn_entry || value->static_value.special != ConstValSpecialRuntime) {
-        ConstExprValue *val = ir_resolve_const(ira, value);
-        if (!val)
+static TypeTableEntry *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruction *instruction,
+    TypeTableEntry *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields,
+    bool depends_on_compile_var)
+{
+    size_t actual_field_count = container_type->data.structure.src_field_count;
+
+    IrInstruction *first_non_const_instruction = nullptr;
+
+    AstNode **field_assign_nodes = allocate<AstNode *>(actual_field_count);
+
+    IrInstructionStructInitField *new_fields = allocate<IrInstructionStructInitField>(actual_field_count);
+
+    FnTableEntry *fn_entry = instruction->source_node->block_context->fn_entry;
+    bool outside_fn = (fn_entry == nullptr);
+
+    ConstExprValue const_val = {};
+    const_val.special = ConstValSpecialStatic;
+    const_val.depends_on_compile_var = depends_on_compile_var;
+    const_val.data.x_struct.fields = allocate<ConstExprValue>(actual_field_count);
+    for (size_t i = 0; i < instr_field_count; i += 1) {
+        IrInstructionContainerInitFieldsField *field = &fields[i];
+
+        IrInstruction *field_value = field->value->other;
+        if (field_value->type_entry->id == TypeTableEntryIdInvalid)
+            return ira->codegen->builtin_types.entry_invalid;
+
+        TypeStructField *type_field = find_struct_type_field(container_type, field->name);
+        if (!type_field) {
+            add_node_error(ira->codegen, field->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;
+        }
+
+        if (type_field->type_entry->id == TypeTableEntryIdInvalid)
             return ira->codegen->builtin_types.entry_invalid;
-        return ir_analyze_const_ptr(ira, &ref_instruction->base, val, value->type_entry, false);
+
+        size_t field_index = type_field->src_index;
+        AstNode *existing_assign_node = field_assign_nodes[field_index];
+        if (existing_assign_node) {
+            ErrorMsg *msg = add_node_error(ira->codegen, field->source_node, buf_sprintf("duplicate field"));
+            add_error_note(ira->codegen, msg, existing_assign_node, buf_sprintf("other field here"));
+            continue;
+        }
+        field_assign_nodes[field_index] = field->source_node;
+
+        new_fields[field_index].value = field_value;
+        new_fields[field_index].type_struct_field = type_field;
+
+        if (const_val.special == ConstValSpecialStatic) {
+            if (outside_fn || field_value->static_value.special != ConstValSpecialRuntime) {
+                ConstExprValue *field_val = ir_resolve_const(ira, field_value);
+                if (!field_val)
+                    return ira->codegen->builtin_types.entry_invalid;
+
+                const_val.data.x_struct.fields[field_index] = *field_val;
+                const_val.depends_on_compile_var = const_val.depends_on_compile_var || field_val->depends_on_compile_var;
+            } else {
+                first_non_const_instruction = field_value;
+                const_val.special = ConstValSpecialRuntime;
+            }
+        }
     }
 
-    TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, value->type_entry, true);
-    if (handle_is_ptr(value->type_entry)) {
-        // this instruction is a noop - codegen can pass the pointer we already have as the result
-        ir_link_new_instruction(value, &ref_instruction->base);
-        return ptr_type;
-    } else {
-        fn_entry->alloca_list.append(&ref_instruction->base);
-        ir_build_ref_from(&ira->new_irb, &ref_instruction->base, value);
-        return ptr_type;
+    bool any_missing = false;
+    for (size_t i = 0; i < actual_field_count; i += 1) {
+        if (!field_assign_nodes[i]) {
+            add_node_error(ira->codegen, instruction->source_node,
+                buf_sprintf("missing field: '%s'", buf_ptr(container_type->data.structure.fields[i].name)));
+            any_missing = true;
+        }
+    }
+    if (any_missing)
+        return ira->codegen->builtin_types.entry_invalid;
+
+    if (const_val.special == ConstValSpecialStatic) {
+        ConstExprValue *out_val = ir_build_const_from(ira, instruction, const_val.depends_on_compile_var);
+        *out_val = const_val;
+        return container_type;
+    }
+
+    if (outside_fn) {
+        add_node_error(ira->codegen, first_non_const_instruction->source_node,
+            buf_sprintf("unable to evaluate constant expression"));
+        return ira->codegen->builtin_types.entry_invalid;
     }
+
+    IrInstruction *new_instruction = ir_build_struct_init_from(&ira->new_irb, instruction,
+        container_type, actual_field_count, new_fields);
+    fn_entry->alloca_list.append(new_instruction);
+    return container_type;
 }
 
-static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
-    switch (instruction->id) {
-        case IrInstructionIdInvalid:
-            zig_unreachable();
-        case IrInstructionIdReturn:
-            return ir_analyze_instruction_return(ira, (IrInstructionReturn *)instruction);
-        case IrInstructionIdConst:
-            return ir_analyze_instruction_const(ira, (IrInstructionConst *)instruction);
-        case IrInstructionIdUnOp:
-            return ir_analyze_instruction_un_op(ira, (IrInstructionUnOp *)instruction);
-        case IrInstructionIdBinOp:
-            return ir_analyze_instruction_bin_op(ira, (IrInstructionBinOp *)instruction);
-        case IrInstructionIdDeclVar:
-            return ir_analyze_instruction_decl_var(ira, (IrInstructionDeclVar *)instruction);
-        case IrInstructionIdLoadPtr:
-            return ir_analyze_instruction_load_ptr(ira, (IrInstructionLoadPtr *)instruction);
-        case IrInstructionIdStorePtr:
-            return ir_analyze_instruction_store_ptr(ira, (IrInstructionStorePtr *)instruction);
-        case IrInstructionIdElemPtr:
-            return ir_analyze_instruction_elem_ptr(ira, (IrInstructionElemPtr *)instruction);
-        case IrInstructionIdVarPtr:
-            return ir_analyze_instruction_var_ptr(ira, (IrInstructionVarPtr *)instruction);
+static TypeTableEntry *ir_analyze_instruction_container_init_list(IrAnalyze *ira, IrInstructionContainerInitList *instruction) {
+    IrInstruction *container_type_value = instruction->container_type->other;
+    TypeTableEntry *container_type = ir_resolve_type(ira, container_type_value);
+    if (!container_type)
+        return ira->codegen->builtin_types.entry_invalid;
+
+    size_t elem_count = instruction->item_count;
+    bool depends_on_compile_var = container_type_value->static_value.depends_on_compile_var;
+
+    if (container_type->id == TypeTableEntryIdStruct && !is_slice(container_type) && elem_count == 0) {
+        return ir_analyze_container_init_fields(ira, &instruction->base, container_type, 0, nullptr, depends_on_compile_var);
+    } else if (is_slice(container_type)) {
+        TypeTableEntry *pointer_type = container_type->data.structure.fields[slice_ptr_index].type_entry;
+        assert(pointer_type->id == TypeTableEntryIdPointer);
+        TypeTableEntry *child_type = pointer_type->data.pointer.child_type;
+
+        ConstExprValue const_val = {};
+        const_val.special = ConstValSpecialStatic;
+        const_val.depends_on_compile_var = depends_on_compile_var;
+        const_val.data.x_array.elements = allocate<ConstExprValue>(elem_count);
+        const_val.data.x_array.size = elem_count;
+
+        FnTableEntry *fn_entry = instruction->base.source_node->block_context->fn_entry;
+        bool outside_fn = (fn_entry == nullptr);
+
+        IrInstruction **new_items = allocate<IrInstruction *>(elem_count);
+
+        IrInstruction *first_non_const_instruction = nullptr;
+
+        for (size_t i = 0; i < elem_count; i += 1) {
+            IrInstruction *arg_value = instruction->items[i]->other;
+            if (arg_value->type_entry->id == TypeTableEntryIdInvalid)
+                return ira->codegen->builtin_types.entry_invalid;
+
+            new_items[i] = arg_value;
+
+            if (const_val.special == ConstValSpecialStatic) {
+                if (outside_fn || arg_value->static_value.special != ConstValSpecialRuntime) {
+                    ConstExprValue *elem_val = ir_resolve_const(ira, arg_value);
+                    if (!elem_val)
+                        return ira->codegen->builtin_types.entry_invalid;
+
+                    const_val.data.x_array.elements[i] = *elem_val;
+                    const_val.depends_on_compile_var = const_val.depends_on_compile_var || elem_val->depends_on_compile_var;
+                } else {
+                    first_non_const_instruction = arg_value;
+                    const_val.special = ConstValSpecialRuntime;
+                }
+            }
+        }
+
+        TypeTableEntry *fixed_size_array_type = get_array_type(ira->codegen, child_type, elem_count);
+        if (const_val.special == ConstValSpecialStatic) {
+            ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, const_val.depends_on_compile_var);
+            *out_val = const_val;
+            return fixed_size_array_type;
+        }
+
+        if (outside_fn) {
+            add_node_error(ira->codegen, first_non_const_instruction->source_node,
+                buf_sprintf("unable to evaluate constant expression"));
+            return ira->codegen->builtin_types.entry_invalid;
+        }
+
+        IrInstruction *new_instruction = ir_build_container_init_list_from(&ira->new_irb, &instruction->base,
+            container_type_value, elem_count, new_items);
+        fn_entry->alloca_list.append(new_instruction);
+        return fixed_size_array_type;
+    } else if (container_type->id == TypeTableEntryIdArray) {
+        // same as slice init but we make a compile error if the length is wrong
+        zig_panic("TODO array container init");
+    } else if (container_type->id == TypeTableEntryIdVoid) {
+        if (elem_count != 0) {
+            add_node_error(ira->codegen, instruction->base.source_node,
+                buf_sprintf("void expression expects no arguments"));
+            return ira->codegen->builtin_types.entry_invalid;
+        }
+        return ir_analyze_void(ira, &instruction->base);
+    } else {
+        add_node_error(ira->codegen, instruction->base.source_node,
+            buf_sprintf("type '%s' does not support array initialization",
+                buf_ptr(&container_type->name)));
+        return ira->codegen->builtin_types.entry_invalid;
+    }
+}
+
+static TypeTableEntry *ir_analyze_instruction_container_init_fields(IrAnalyze *ira, IrInstructionContainerInitFields *instruction) {
+    IrInstruction *container_type_value = instruction->container_type->other;
+    TypeTableEntry *container_type = ir_resolve_type(ira, container_type_value);
+    if (!container_type)
+        return ira->codegen->builtin_types.entry_invalid;
+
+    bool depends_on_compile_var = container_type_value->static_value.depends_on_compile_var;
+
+    return ir_analyze_container_init_fields(ira, &instruction->base, container_type,
+        instruction->field_count, instruction->fields, depends_on_compile_var);
+}
+
+static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
+    switch (instruction->id) {
+        case IrInstructionIdInvalid:
+            zig_unreachable();
+        case IrInstructionIdReturn:
+            return ir_analyze_instruction_return(ira, (IrInstructionReturn *)instruction);
+        case IrInstructionIdConst:
+            return ir_analyze_instruction_const(ira, (IrInstructionConst *)instruction);
+        case IrInstructionIdUnOp:
+            return ir_analyze_instruction_un_op(ira, (IrInstructionUnOp *)instruction);
+        case IrInstructionIdBinOp:
+            return ir_analyze_instruction_bin_op(ira, (IrInstructionBinOp *)instruction);
+        case IrInstructionIdDeclVar:
+            return ir_analyze_instruction_decl_var(ira, (IrInstructionDeclVar *)instruction);
+        case IrInstructionIdLoadPtr:
+            return ir_analyze_instruction_load_ptr(ira, (IrInstructionLoadPtr *)instruction);
+        case IrInstructionIdStorePtr:
+            return ir_analyze_instruction_store_ptr(ira, (IrInstructionStorePtr *)instruction);
+        case IrInstructionIdElemPtr:
+            return ir_analyze_instruction_elem_ptr(ira, (IrInstructionElemPtr *)instruction);
+        case IrInstructionIdVarPtr:
+            return ir_analyze_instruction_var_ptr(ira, (IrInstructionVarPtr *)instruction);
         case IrInstructionIdFieldPtr:
             return ir_analyze_instruction_field_ptr(ira, (IrInstructionFieldPtr *)instruction);
         case IrInstructionIdCall:
@@ -5844,10 +6280,14 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
             return ir_analyze_instruction_array_len(ira, (IrInstructionArrayLen *)instruction);
         case IrInstructionIdRef:
             return ir_analyze_instruction_ref(ira, (IrInstructionRef *)instruction);
-        case IrInstructionIdCast:
         case IrInstructionIdContainerInitList:
+            return ir_analyze_instruction_container_init_list(ira, (IrInstructionContainerInitList *)instruction);
         case IrInstructionIdContainerInitFields:
+            return ir_analyze_instruction_container_init_fields(ira, (IrInstructionContainerInitFields *)instruction);
+        case IrInstructionIdCast:
         case IrInstructionIdStructFieldPtr:
+        case IrInstructionIdEnumFieldPtr:
+        case IrInstructionIdStructInit:
             zig_panic("TODO analyze more instructions");
     }
     zig_unreachable();
@@ -5947,6 +6387,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
         case IrInstructionIdCast:
         case IrInstructionIdContainerInitList:
         case IrInstructionIdContainerInitFields:
+        case IrInstructionIdStructInit:
         case IrInstructionIdFieldPtr:
         case IrInstructionIdElemPtr:
         case IrInstructionIdVarPtr:
@@ -5955,6 +6396,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
         case IrInstructionIdPtrTypeChild:
         case IrInstructionIdArrayLen:
         case IrInstructionIdStructFieldPtr:
+        case IrInstructionIdEnumFieldPtr:
         case IrInstructionIdArrayType:
         case IrInstructionIdSliceType:
         case IrInstructionIdCompileVar:
@@ -6393,46 +6835,6 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
 //    return g->builtin_types.entry_void;
 //}
 //
-//static TypeTableEntry *analyze_set_fn_static_eval(CodeGen *g, ImportTableEntry *import,
-//        BlockContext *context, AstNode *node)
-//{
-//    AstNode **fn_node = &node->data.fn_call_expr.params.at(0);
-//    AstNode **value_node = &node->data.fn_call_expr.params.at(1);
-//
-//    FnTableEntry *fn_entry = resolve_const_expr_fn(g, import, context, fn_node);
-//    if (!fn_entry) {
-//        return g->builtin_types.entry_invalid;
-//    }
-//
-//    bool want_static_eval;
-//    bool ok = resolve_const_expr_bool(g, import, context, value_node, &want_static_eval);
-//    if (!ok) {
-//        return g->builtin_types.entry_invalid;
-//    }
-//
-//    if (fn_entry->fn_static_eval_set_node) {
-//        ErrorMsg *msg = add_node_error(g, node, buf_sprintf("function static eval attribute set twice"));
-//        add_error_note(g, msg, fn_entry->fn_static_eval_set_node, buf_sprintf("first set here"));
-//        return g->builtin_types.entry_invalid;
-//    }
-//    fn_entry->fn_static_eval_set_node = node;
-//
-//    if (want_static_eval && !context->fn_entry->is_pure) {
-//        add_node_error(g, node, buf_sprintf("attribute appears too late within function"));
-//        return g->builtin_types.entry_invalid;
-//    }
-//
-//    if (want_static_eval) {
-//        fn_entry->want_pure = WantPureTrue;
-//        fn_entry->want_pure_attr_node = node;
-//    } else {
-//        fn_entry->want_pure = WantPureFalse;
-//        fn_entry->is_pure = false;
-//    }
-//
-//    return g->builtin_types.entry_void;
-//}
-//
 //static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
 //        TypeTableEntry *expected_type, AstNode *node)
 //{
@@ -6634,713 +7036,10 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
 //            return analyze_set_fn_test(g, import, context, node);
 //        case BuiltinFnIdSetFnNoInline:
 //            return analyze_set_fn_no_inline(g, import, context, node);
-//        case BuiltinFnIdSetFnStaticEval:
-//            return analyze_set_fn_static_eval(g, import, context, node);
 //    }
 //    zig_unreachable();
 //}
 
-//static TypeTableEntry *analyze_container_init_expr(CodeGen *g, ImportTableEntry *import,
-//    BlockContext *context, AstNode *node)
-//{
-//    assert(node->type == NodeTypeContainerInitExpr);
-//
-//    AstNodeContainerInitExpr *container_init_expr = &node->data.container_init_expr;
-//
-//    ContainerInitKind kind = container_init_expr->kind;
-//
-//    if (container_init_expr->type->type == NodeTypeFieldAccessExpr) {
-//        container_init_expr->type->data.field_access_expr.container_init_expr_node = node;
-//    }
-//
-//    TypeTableEntry *container_meta_type = analyze_expression(g, import, context, nullptr,
-//            container_init_expr->type);
-//
-//    if (container_meta_type->id == TypeTableEntryIdInvalid) {
-//        return g->builtin_types.entry_invalid;
-//    }
-//
-//    if (node->data.container_init_expr.enum_type) {
-//        get_resolved_expr(node)->const_val = get_resolved_expr(container_init_expr->type)->const_val;
-//        return node->data.container_init_expr.enum_type;
-//    }
-//
-//    TypeTableEntry *container_type = resolve_type(g, container_init_expr->type);
-//
-//    if (container_type->id == TypeTableEntryIdInvalid) {
-//        return container_type;
-//    } else if (container_type->id == TypeTableEntryIdStruct &&
-//               !container_type->data.structure.is_slice &&
-//               (kind == ContainerInitKindStruct || (kind == ContainerInitKindArray &&
-//                                                    container_init_expr->entries.length == 0)))
-//    {
-//        StructValExprCodeGen *codegen = &container_init_expr->resolved_struct_val_expr;
-//        codegen->type_entry = container_type;
-//        codegen->source_node = node;
-//
-//
-//        size_t expr_field_count = container_init_expr->entries.length;
-//        size_t actual_field_count = container_type->data.structure.src_field_count;
-//
-//        AstNode *non_const_expr_culprit = nullptr;
-//
-//        size_t *field_use_counts = allocate<size_t>(actual_field_count);
-//        ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
-//        const_val->ok = true;
-//        const_val->data.x_struct.fields = allocate<ConstExprValue*>(actual_field_count);
-//        for (size_t i = 0; i < expr_field_count; i += 1) {
-//            AstNode *val_field_node = container_init_expr->entries.at(i);
-//            assert(val_field_node->type == NodeTypeStructValueField);
-//
-//            val_field_node->block_context = context;
-//
-//            TypeStructField *type_field = find_struct_type_field(container_type,
-//                    val_field_node->data.struct_val_field.name);
-//
-//            if (!type_field) {
-//                add_node_error(g, val_field_node,
-//                    buf_sprintf("no member named '%s' in '%s'",
-//                        buf_ptr(val_field_node->data.struct_val_field.name), buf_ptr(&container_type->name)));
-//                continue;
-//            }
-//
-//            if (type_field->type_entry->id == TypeTableEntryIdInvalid) {
-//                return g->builtin_types.entry_invalid;
-//            }
-//
-//            size_t field_index = type_field->src_index;
-//            field_use_counts[field_index] += 1;
-//            if (field_use_counts[field_index] > 1) {
-//                add_node_error(g, val_field_node, buf_sprintf("duplicate field"));
-//                continue;
-//            }
-//
-//            val_field_node->data.struct_val_field.type_struct_field = type_field;
-//
-//            analyze_expression(g, import, context, type_field->type_entry,
-//                    val_field_node->data.struct_val_field.expr);
-//
-//            if (const_val->ok) {
-//                ConstExprValue *field_val =
-//                    &get_resolved_expr(val_field_node->data.struct_val_field.expr)->const_val;
-//                if (field_val->ok) {
-//                    const_val->data.x_struct.fields[field_index] = field_val;
-//                    const_val->depends_on_compile_var = const_val->depends_on_compile_var || field_val->depends_on_compile_var;
-//                } else {
-//                    const_val->ok = false;
-//                    non_const_expr_culprit = val_field_node->data.struct_val_field.expr;
-//                }
-//            }
-//        }
-//        if (!const_val->ok) {
-//            assert(non_const_expr_culprit);
-//            if (context->fn_entry) {
-//                context->fn_entry->struct_val_expr_alloca_list.append(codegen);
-//            } else {
-//                add_node_error(g, non_const_expr_culprit, buf_sprintf("unable to evaluate constant expression"));
-//            }
-//        }
-//
-//        for (size_t i = 0; i < actual_field_count; i += 1) {
-//            if (field_use_counts[i] == 0) {
-//                add_node_error(g, node,
-//                    buf_sprintf("missing field: '%s'", buf_ptr(container_type->data.structure.fields[i].name)));
-//            }
-//        }
-//        return container_type;
-//    } else if (container_type->id == TypeTableEntryIdStruct &&
-//               container_type->data.structure.is_slice &&
-//               kind == ContainerInitKindArray)
-//    {
-//        size_t elem_count = container_init_expr->entries.length;
-//
-//        TypeTableEntry *pointer_type = container_type->data.structure.fields[0].type_entry;
-//        assert(pointer_type->id == TypeTableEntryIdPointer);
-//        TypeTableEntry *child_type = pointer_type->data.pointer.child_type;
-//
-//        ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
-//        const_val->ok = true;
-//        const_val->data.x_array.fields = allocate<ConstExprValue*>(elem_count);
-//
-//        for (size_t i = 0; i < elem_count; i += 1) {
-//            AstNode **elem_node = &container_init_expr->entries.at(i);
-//            analyze_expression(g, import, context, child_type, *elem_node);
-//
-//            if (const_val->ok) {
-//                ConstExprValue *elem_const_val = &get_resolved_expr(*elem_node)->const_val;
-//                if (elem_const_val->ok) {
-//                    const_val->data.x_array.fields[i] = elem_const_val;
-//                    const_val->depends_on_compile_var = const_val->depends_on_compile_var ||
-//                        elem_const_val->depends_on_compile_var;
-//                } else {
-//                    const_val->ok = false;
-//                }
-//            }
-//        }
-//
-//        TypeTableEntry *fixed_size_array_type = get_array_type(g, child_type, elem_count);
-//
-//        StructValExprCodeGen *codegen = &container_init_expr->resolved_struct_val_expr;
-//        codegen->type_entry = fixed_size_array_type;
-//        codegen->source_node = node;
-//        if (!const_val->ok) {
-//            if (!context->fn_entry) {
-//                add_node_error(g, node,
-//                    buf_sprintf("unable to evaluate constant expression"));
-//            } else {
-//                context->fn_entry->struct_val_expr_alloca_list.append(codegen);
-//            }
-//        }
-//
-//        return fixed_size_array_type;
-//    } else if (container_type->id == TypeTableEntryIdArray) {
-//        zig_panic("TODO array container init");
-//        return container_type;
-//    } else if (container_type->id == TypeTableEntryIdVoid) {
-//        if (container_init_expr->entries.length != 0) {
-//            add_node_error(g, node, buf_sprintf("void expression expects no arguments"));
-//            return g->builtin_types.entry_invalid;
-//        } else {
-//            return resolve_expr_const_val_as_void(g, node);
-//        }
-//    } else {
-//        add_node_error(g, node,
-//            buf_sprintf("type '%s' does not support %s initialization syntax",
-//                buf_ptr(&container_type->name), err_container_init_syntax_name(kind)));
-//        return g->builtin_types.entry_invalid;
-//    }
-//}
-
-
-
-//static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
-//        TypeTableEntry *expected_type, AstNode *node)
-//{
-//    assert(node->type == NodeTypeFieldAccessExpr);
-//
-//    AstNode **struct_expr_node = &node->data.field_access_expr.struct_expr;
-//    TypeTableEntry *struct_type = analyze_expression(g, import, context, nullptr, *struct_expr_node);
-//    Buf *field_name = node->data.field_access_expr.field_name;
-//
-//    if (struct_type->id == TypeTableEntryIdInvalid) {
-//        return struct_type;
-//    } else if (is_container_ref(struct_type)) {
-//        return analyze_container_member_access(g, field_name, node, struct_type);
-//    } else if (struct_type->id == TypeTableEntryIdArray) {
-//        if (buf_eql_str(field_name, "len")) {
-//            return resolve_expr_const_val_as_unsigned_num_lit(g, node, expected_type,
-//                    struct_type->data.array.len, false);
-//        } else {
-//            add_node_error(g, node,
-//                buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name),
-//                    buf_ptr(&struct_type->name)));
-//            return g->builtin_types.entry_invalid;
-//        }
-//    } else if (struct_type->id == TypeTableEntryIdMetaType) {
-//        TypeTableEntry *child_type = resolve_type(g, *struct_expr_node);
-//
-//        if (child_type->id == TypeTableEntryIdInvalid) {
-//            return g->builtin_types.entry_invalid;
-//        } else if (child_type->id == TypeTableEntryIdEnum) {
-//            AstNode *container_init_node = node->data.field_access_expr.container_init_expr_node;
-//            AstNode *value_node;
-//            if (container_init_node) {
-//                assert(container_init_node->type == NodeTypeContainerInitExpr);
-//                size_t param_count = container_init_node->data.container_init_expr.entries.length;
-//                if (param_count > 1) {
-//                    AstNode *first_invalid_node = container_init_node->data.container_init_expr.entries.at(1);
-//                    add_node_error(g, first_executing_node(first_invalid_node),
-//                            buf_sprintf("enum values accept only one parameter"));
-//                    return child_type;
-//                } else {
-//                    if (param_count == 1) {
-//                        value_node = container_init_node->data.container_init_expr.entries.at(0);
-//                    } else {
-//                        value_node = nullptr;
-//                    }
-//                    container_init_node->data.container_init_expr.enum_type = child_type;
-//                }
-//            } else {
-//                value_node = nullptr;
-//            }
-//            return analyze_enum_value_expr(g, import, context, node, value_node, child_type, field_name, node);
-//        } else if (child_type->id == TypeTableEntryIdStruct) {
-//            BlockContext *container_block_context = get_container_block_context(child_type);
-//            auto entry = container_block_context->decl_table.maybe_get(field_name);
-//            AstNode *decl_node = entry ? entry->value : nullptr;
-//            if (decl_node) {
-//                bool pointer_only = false;
-//                return analyze_decl_ref(g, node, decl_node, pointer_only, context, false);
-//            } else {
-//                add_node_error(g, node,
-//                    buf_sprintf("container '%s' has no member called '%s'",
-//                        buf_ptr(&child_type->name), buf_ptr(field_name)));
-//                return g->builtin_types.entry_invalid;
-//            }
-//        } else if (child_type->id == TypeTableEntryIdPureError) {
-//            return analyze_error_literal_expr(g, import, context, node, field_name);
-//        } else if (child_type->id == TypeTableEntryIdInt) {
-//            bool depends_on_compile_var =
-//                get_resolved_expr(*struct_expr_node)->const_val.depends_on_compile_var;
-//            if (buf_eql_str(field_name, "bit_count")) {
-//                return resolve_expr_const_val_as_unsigned_num_lit(g, node, expected_type,
-//                        child_type->data.integral.bit_count, depends_on_compile_var);
-//            } else if (buf_eql_str(field_name, "is_signed")) {
-//                return resolve_expr_const_val_as_bool(g, node, child_type->data.integral.is_signed,
-//                        depends_on_compile_var);
-//            } else {
-//                add_node_error(g, node,
-//                    buf_sprintf("type '%s' has no member called '%s'",
-//                        buf_ptr(&child_type->name), buf_ptr(field_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)));
-//            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)));
-//        return g->builtin_types.entry_invalid;
-//    }
-//}
-//
-//static TypeTableEntry *bad_method_call(CodeGen *g, AstNode *node, TypeTableEntry *container_type,
-//        TypeTableEntry *expected_param_type, FnTableEntry *fn_table_entry)
-//{
-//    ErrorMsg *msg = add_node_error(g, node,
-//        buf_sprintf("function called as method of '%s', but first parameter is of type '%s'",
-//            buf_ptr(&container_type->name),
-//            buf_ptr(&expected_param_type->name)));
-//    if (fn_table_entry) {
-//        add_error_note(g, msg, fn_table_entry->proto_node, buf_sprintf("function declared here"));
-//    }
-//    return g->builtin_types.entry_invalid;
-//}
-//
-//// Before calling this function, set node->data.fn_call_expr.fn_table_entry if the function is known
-//// at compile time. Otherwise this is a function pointer call.
-//static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
-//        TypeTableEntry *expected_type, AstNode *node, TypeTableEntry *fn_type,
-//        AstNode *struct_node)
-//{
-//    assert(node->type == NodeTypeFnCallExpr);
-//
-//    if (fn_type->id == TypeTableEntryIdInvalid) {
-//        return fn_type;
-//    }
-//
-//    // The function call might include inline parameters which we need to ignore according to the
-//    // fn_type.
-//    FnTableEntry *fn_table_entry = node->data.fn_call_expr.fn_entry;
-//    AstNode *generic_proto_node = fn_table_entry ?
-//        fn_table_entry->proto_node->data.fn_proto.generic_proto_node : nullptr;
-//
-//    // count parameters
-//    size_t struct_node_1_or_0 = struct_node ? 1 : 0;
-//    size_t src_param_count = fn_type->data.fn.fn_type_id.param_count +
-//        (generic_proto_node ? generic_proto_node->data.fn_proto.inline_arg_count : 0);
-//    size_t call_param_count = node->data.fn_call_expr.params.length;
-//    size_t expect_arg_count = src_param_count - struct_node_1_or_0;
-//
-//    bool ok_invocation = true;
-//
-//    if (fn_type->data.fn.fn_type_id.is_var_args) {
-//        if (call_param_count < expect_arg_count) {
-//            ok_invocation = false;
-//            add_node_error(g, node,
-//                buf_sprintf("expected at least %zu arguments, found %zu", src_param_count, call_param_count));
-//        }
-//    } else if (expect_arg_count != call_param_count) {
-//        ok_invocation = false;
-//        add_node_error(g, node,
-//                buf_sprintf("expected %zu arguments, found %zu", expect_arg_count, call_param_count));
-//    }
-//
-//    bool all_args_const_expr = true;
-//
-//    if (struct_node) {
-//        Expr *struct_expr = get_resolved_expr(struct_node);
-//        ConstExprValue *struct_const_val = &struct_expr->const_val;
-//        if (!struct_const_val->ok) {
-//            all_args_const_expr = false;
-//        }
-//
-//        FnTypeParamInfo *param_info = &fn_type->data.fn.fn_type_id.param_info[0];
-//        TypeTableEntry *expected_param_type = param_info->type;
-//        TypeTableEntry *container_bare_type = container_ref_type(struct_expr->type_entry);
-//        if (is_container_ref(expected_param_type)) {
-//            TypeTableEntry *param_bare_type = container_ref_type(expected_param_type);
-//            if (param_bare_type != container_bare_type) {
-//                return bad_method_call(g, node, container_bare_type, expected_param_type, fn_table_entry);
-//            }
-//        } else {
-//            return bad_method_call(g, node, container_bare_type, expected_param_type, fn_table_entry);
-//        }
-//    }
-//
-//    // analyze each parameter. in the case of a method, we already analyzed the
-//    // first parameter in order to figure out which struct we were calling a method on.
-//    size_t next_type_i = struct_node_1_or_0;
-//    for (size_t call_i = 0; call_i < call_param_count; call_i += 1) {
-//        size_t proto_i = call_i + struct_node_1_or_0;
-//        AstNode **param_node = &node->data.fn_call_expr.params.at(call_i);
-//        // determine the expected type for each parameter
-//        TypeTableEntry *expected_param_type = nullptr;
-//        if (proto_i < src_param_count) {
-//            if (generic_proto_node &&
-//                generic_proto_node->data.fn_proto.params.at(proto_i)->data.param_decl.is_inline)
-//            {
-//                continue;
-//            }
-//
-//            FnTypeParamInfo *param_info = &fn_type->data.fn.fn_type_id.param_info[next_type_i];
-//            next_type_i += 1;
-//
-//            expected_param_type = param_info->type;
-//        }
-//        TypeTableEntry *param_type = analyze_expression(g, import, context, expected_param_type, *param_node);
-//        if (param_type->id == TypeTableEntryIdInvalid) {
-//            return param_type;
-//        }
-//
-//        ConstExprValue *const_arg_val = &get_resolved_expr(*param_node)->const_val;
-//        if (!const_arg_val->ok) {
-//            all_args_const_expr = false;
-//        }
-//    }
-//
-//    TypeTableEntry *return_type = fn_type->data.fn.fn_type_id.return_type;
-//
-//    if (return_type->id == TypeTableEntryIdInvalid) {
-//        return return_type;
-//    }
-//
-//    ConstExprValue *result_val = &get_resolved_expr(node)->const_val;
-//    if (ok_invocation && fn_table_entry && fn_table_entry->is_pure && fn_table_entry->want_pure != WantPureFalse) {
-//        if (fn_table_entry->anal_state == FnAnalStateReady) {
-//            analyze_fn_body(g, fn_table_entry);
-//            if (fn_table_entry->proto_node->data.fn_proto.skip) {
-//                return g->builtin_types.entry_invalid;
-//            }
-//        }
-//        if (all_args_const_expr) {
-//            if (fn_table_entry->is_pure && fn_table_entry->anal_state == FnAnalStateComplete) {
-//                if (eval_fn(g, node, fn_table_entry, result_val, 1000, struct_node)) {
-//                    // function evaluation generated an error
-//                    return g->builtin_types.entry_invalid;
-//                }
-//                return return_type;
-//            }
-//        }
-//    }
-//    if (!ok_invocation || !fn_table_entry || !fn_table_entry->is_pure || fn_table_entry->want_pure == WantPureFalse) {
-//        // calling an impure fn is impure
-//        mark_impure_fn(g, context, node);
-//        if (fn_table_entry && fn_table_entry->want_pure == WantPureTrue) {
-//            return g->builtin_types.entry_invalid;
-//        }
-//    }
-//
-//    // TODO
-//    //if (handle_is_ptr(return_type)) {
-//    //    if (context->fn_entry) {
-//    //        context->fn_entry->cast_alloca_list.append(node);
-//    //    } else if (!result_val->ok) {
-//    //        add_node_error(g, node, buf_sprintf("unable to evaluate constant expression"));
-//    //    }
-//    //}
-//
-//    return return_type;
-//}
-//
-//static TypeTableEntry *analyze_fn_call_with_inline_args(CodeGen *g, ImportTableEntry *import,
-//        BlockContext *parent_context, TypeTableEntry *expected_type, AstNode *call_node,
-//        FnTableEntry *fn_table_entry, AstNode *struct_node)
-//{
-//    assert(call_node->type == NodeTypeFnCallExpr);
-//    assert(fn_table_entry);
-//
-//    AstNode *decl_node = fn_table_entry->proto_node;
-//
-//    // count parameters
-//    size_t struct_node_1_or_0 = (struct_node ? 1 : 0);
-//    size_t src_param_count = decl_node->data.fn_proto.params.length;
-//    size_t call_param_count = call_node->data.fn_call_expr.params.length;
-//
-//    if (src_param_count != call_param_count + struct_node_1_or_0) {
-//        add_node_error(g, call_node,
-//            buf_sprintf("expected %zu arguments, found %zu", src_param_count - struct_node_1_or_0, call_param_count));
-//        return g->builtin_types.entry_invalid;
-//    }
-//
-//    size_t inline_or_var_type_arg_count = decl_node->data.fn_proto.inline_or_var_type_arg_count;
-//    assert(inline_or_var_type_arg_count > 0);
-//
-//    BlockContext *child_context = decl_node->owner->block_context;
-//    size_t next_generic_param_index = 0;
-//
-//    GenericFnTypeId *generic_fn_type_id = allocate<GenericFnTypeId>(1);
-//    generic_fn_type_id->decl_node = decl_node;
-//    generic_fn_type_id->generic_param_count = inline_or_var_type_arg_count;
-//    generic_fn_type_id->generic_params = allocate<GenericParamValue>(inline_or_var_type_arg_count);
-//
-//    size_t next_impl_i = 0;
-//    for (size_t call_i = 0; call_i < call_param_count; call_i += 1) {
-//        size_t proto_i = call_i + struct_node_1_or_0;
-//        AstNode *generic_param_decl_node = decl_node->data.fn_proto.params.at(proto_i);
-//        assert(generic_param_decl_node->type == NodeTypeParamDecl);
-//
-//        AstNode **generic_param_type_node = &generic_param_decl_node->data.param_decl.type;
-//        TypeTableEntry *expected_param_type = analyze_type_expr(g, decl_node->owner, child_context,
-//                *generic_param_type_node);
-//        if (expected_param_type->id == TypeTableEntryIdInvalid) {
-//            return expected_param_type;
-//        }
-//
-//        bool is_var_type = (expected_param_type->id == TypeTableEntryIdVar);
-//        bool is_inline = generic_param_decl_node->data.param_decl.is_inline;
-//        if (!is_inline && !is_var_type) {
-//            next_impl_i += 1;
-//            continue;
-//        }
-//
-//
-//        AstNode **param_node = &call_node->data.fn_call_expr.params.at(call_i);
-//        TypeTableEntry *param_type = analyze_expression(g, import, parent_context,
-//                is_var_type ? nullptr : expected_param_type, *param_node);
-//        if (param_type->id == TypeTableEntryIdInvalid) {
-//            return param_type;
-//        }
-//
-//        // set child_context so that the previous param is in scope
-//        child_context = new_block_context(generic_param_decl_node, child_context);
-//
-//        ConstExprValue *const_val = &get_resolved_expr(*param_node)->const_val;
-//        if (is_inline && !const_val->ok) {
-//            add_node_error(g, *param_node,
-//                    buf_sprintf("unable to evaluate constant expression for inline parameter"));
-//
-//            return g->builtin_types.entry_invalid;
-//        }
-//
-//        VariableTableEntry *var = add_local_var_shadowable(g, generic_param_decl_node, decl_node->owner, child_context,
-//                generic_param_decl_node->data.param_decl.name, param_type, true, *param_node, true);
-//        // This generic function instance could be called with anything, so when this variable is read it
-//        // needs to know that it depends on compile time variable data.
-//        var->force_depends_on_compile_var = true;
-//
-//        GenericParamValue *generic_param_value =
-//            &generic_fn_type_id->generic_params[next_generic_param_index];
-//        generic_param_value->type = param_type;
-//        generic_param_value->node = is_inline ? *param_node : nullptr;
-//        generic_param_value->impl_index = next_impl_i;
-//        next_generic_param_index += 1;
-//
-//        if (!is_inline) {
-//            next_impl_i += 1;
-//        }
-//    }
-//
-//    assert(next_generic_param_index == inline_or_var_type_arg_count);
-//
-//    auto entry = g->generic_table.maybe_get(generic_fn_type_id);
-//    FnTableEntry *impl_fn;
-//    if (entry) {
-//        AstNode *impl_decl_node = entry->value;
-//        assert(impl_decl_node->type == NodeTypeFnProto);
-//        impl_fn = impl_decl_node->data.fn_proto.fn_table_entry;
-//    } else {
-//        AstNode *decl_node = generic_fn_type_id->decl_node;
-//        AstNode *impl_fn_def_node = ast_clone_subtree_special(decl_node->data.fn_proto.fn_def_node,
-//                &g->next_node_index, AstCloneSpecialOmitInlineParams);
-//        AstNode *impl_decl_node = impl_fn_def_node->data.fn_def.fn_proto;
-//        impl_decl_node->data.fn_proto.inline_arg_count = 0;
-//        impl_decl_node->data.fn_proto.inline_or_var_type_arg_count = 0;
-//        impl_decl_node->data.fn_proto.generic_proto_node = decl_node;
-//
-//        // replace var arg types with actual types
-//        for (size_t generic_arg_i = 0; generic_arg_i < inline_or_var_type_arg_count; generic_arg_i += 1) {
-//            GenericParamValue *generic_param_value = &generic_fn_type_id->generic_params[generic_arg_i];
-//            if (!generic_param_value->node) {
-//                size_t impl_i = generic_param_value->impl_index;
-//                AstNode *impl_param_decl_node = impl_decl_node->data.fn_proto.params.at(impl_i);
-//                assert(impl_param_decl_node->type == NodeTypeParamDecl);
-//
-//                impl_param_decl_node->data.param_decl.type = create_ast_type_node(g, import,
-//                        generic_param_value->type, impl_param_decl_node);
-//                normalize_parent_ptrs(impl_param_decl_node);
-//            }
-//        }
-//
-//        preview_fn_proto_instance(g, import, impl_decl_node, child_context);
-//        g->generic_table.put(generic_fn_type_id, impl_decl_node);
-//        impl_fn = impl_decl_node->data.fn_proto.fn_table_entry;
-//    }
-//
-//    call_node->data.fn_call_expr.fn_entry = impl_fn;
-//    return analyze_fn_call_ptr(g, import, parent_context, expected_type, call_node,
-//            impl_fn->type_entry, struct_node);
-//}
-//
-//static TypeTableEntry *analyze_generic_fn_call(CodeGen *g, ImportTableEntry *import, BlockContext *parent_context,
-//        TypeTableEntry *expected_type, AstNode *node, TypeTableEntry *generic_fn_type)
-//{
-//    assert(node->type == NodeTypeFnCallExpr);
-//    assert(generic_fn_type->id == TypeTableEntryIdGenericFn);
-//
-//    AstNode *decl_node = generic_fn_type->data.generic_fn.decl_node;
-//    assert(decl_node->type == NodeTypeContainerDecl);
-//    ZigList<AstNode *> *generic_params = &decl_node->data.struct_decl.generic_params;
-//
-//    size_t expected_param_count = generic_params->length;
-//    size_t actual_param_count = node->data.fn_call_expr.params.length;
-//
-//    if (actual_param_count != expected_param_count) {
-//        add_node_error(g, first_executing_node(node),
-//                buf_sprintf("expected %zu arguments, found %zu", expected_param_count, actual_param_count));
-//        return g->builtin_types.entry_invalid;
-//    }
-//
-//    GenericFnTypeId *generic_fn_type_id = allocate<GenericFnTypeId>(1);
-//    generic_fn_type_id->decl_node = decl_node;
-//    generic_fn_type_id->generic_param_count = actual_param_count;
-//    generic_fn_type_id->generic_params = allocate<GenericParamValue>(actual_param_count);
-//
-//    BlockContext *child_context = decl_node->owner->block_context;
-//    for (size_t i = 0; i < actual_param_count; i += 1) {
-//        AstNode *generic_param_decl_node = generic_params->at(i);
-//        assert(generic_param_decl_node->type == NodeTypeParamDecl);
-//
-//        AstNode **generic_param_type_node = &generic_param_decl_node->data.param_decl.type;
-//
-//        TypeTableEntry *expected_param_type = analyze_type_expr(g, decl_node->owner,
-//                child_context, *generic_param_type_node);
-//        if (expected_param_type->id == TypeTableEntryIdInvalid) {
-//            return expected_param_type;
-//        }
-//
-//
-//
-//        AstNode **param_node = &node->data.fn_call_expr.params.at(i);
-//
-//        TypeTableEntry *param_type = analyze_expression(g, import, parent_context, expected_param_type,
-//                *param_node);
-//        if (param_type->id == TypeTableEntryIdInvalid) {
-//            return param_type;
-//        }
-//
-//        // set child_context so that the previous param is in scope
-//        child_context = new_block_context(generic_param_decl_node, child_context);
-//
-//        ConstExprValue *const_val = &get_resolved_expr(*param_node)->const_val;
-//        if (const_val->ok) {
-//            VariableTableEntry *var = add_local_var(g, generic_param_decl_node, decl_node->owner, child_context,
-//                    generic_param_decl_node->data.param_decl.name, param_type, true, *param_node);
-//            var->force_depends_on_compile_var = true;
-//        } else {
-//            add_node_error(g, *param_node, buf_sprintf("unable to evaluate constant expression"));
-//
-//            return g->builtin_types.entry_invalid;
-//        }
-//
-//        GenericParamValue *generic_param_value = &generic_fn_type_id->generic_params[i];
-//        generic_param_value->type = param_type;
-//        generic_param_value->node = *param_node;
-//    }
-//
-//    auto entry = g->generic_table.maybe_get(generic_fn_type_id);
-//    if (entry) {
-//        AstNode *impl_decl_node = entry->value;
-//        assert(impl_decl_node->type == NodeTypeContainerDecl);
-//        TypeTableEntry *type_entry = impl_decl_node->data.struct_decl.type_entry;
-//        return resolve_expr_const_val_as_type(g, node, type_entry, false);
-//    }
-//
-//    // make a type from the generic parameters supplied
-//    assert(decl_node->type == NodeTypeContainerDecl);
-//    AstNode *impl_decl_node = ast_clone_subtree(decl_node, &g->next_node_index);
-//    g->generic_table.put(generic_fn_type_id, impl_decl_node);
-//    scan_struct_decl(g, import, child_context, impl_decl_node);
-//    TypeTableEntry *type_entry = impl_decl_node->data.struct_decl.type_entry;
-//    resolve_struct_type(g, import, type_entry);
-//    return resolve_expr_const_val_as_type(g, node, type_entry, false);
-//}
-//
-//static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
-//        TypeTableEntry *expected_type, AstNode *node)
-//{
-//    AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
-//
-//    if (node->data.fn_call_expr.is_builtin) {
-//        zig_panic("moved builtin fn call code to ir.cpp");
-//    }
-//
-//    TypeTableEntry *invoke_type_entry = analyze_expression(g, import, context, nullptr, fn_ref_expr);
-//    if (invoke_type_entry->id == TypeTableEntryIdInvalid) {
-//        return g->builtin_types.entry_invalid;
-//    }
-//
-//    // use constant expression evaluator to figure out the function at compile time.
-//    // otherwise we treat this as a function pointer.
-//    ConstExprValue *const_val = &get_resolved_expr(fn_ref_expr)->const_val;
-//
-//    if (const_val->ok) {
-//        if (invoke_type_entry->id == TypeTableEntryIdMetaType) {
-//            zig_unreachable();
-//        } else if (invoke_type_entry->id == TypeTableEntryIdFn) {
-//            AstNode *struct_node;
-//            if (fn_ref_expr->type == NodeTypeFieldAccessExpr &&
-//                fn_ref_expr->data.field_access_expr.is_member_fn)
-//            {
-//                struct_node = fn_ref_expr->data.field_access_expr.struct_expr;
-//            } else {
-//                struct_node = nullptr;
-//            }
-//
-//            FnTableEntry *fn_table_entry = const_val->data.x_fn;
-//            node->data.fn_call_expr.fn_entry = fn_table_entry;
-//            return analyze_fn_call_ptr(g, import, context, expected_type, node,
-//                    fn_table_entry->type_entry, struct_node);
-//        } else if (invoke_type_entry->id == TypeTableEntryIdGenericFn) {
-//            TypeTableEntry *generic_fn_type = const_val->data.x_type;
-//            AstNode *decl_node = generic_fn_type->data.generic_fn.decl_node;
-//            if (decl_node->type == NodeTypeFnProto) {
-//                AstNode *struct_node;
-//                if (fn_ref_expr->type == NodeTypeFieldAccessExpr &&
-//                    fn_ref_expr->data.field_access_expr.is_member_fn)
-//                {
-//                    struct_node = fn_ref_expr->data.field_access_expr.struct_expr;
-//                } else {
-//                    struct_node = nullptr;
-//                }
-//
-//                FnTableEntry *fn_table_entry = decl_node->data.fn_proto.fn_table_entry;
-//                if (fn_table_entry->proto_node->data.fn_proto.skip) {
-//                    return g->builtin_types.entry_invalid;
-//                }
-//                return analyze_fn_call_with_inline_args(g, import, context, expected_type, node,
-//                        fn_table_entry, struct_node);
-//            } else {
-//                return analyze_generic_fn_call(g, import, context, expected_type, node, const_val->data.x_type);
-//            }
-//        } else {
-//            add_node_error(g, fn_ref_expr,
-//                buf_sprintf("type '%s' not a function", buf_ptr(&invoke_type_entry->name)));
-//            return g->builtin_types.entry_invalid;
-//        }
-//    }
-//
-//    // function pointer
-//    if (invoke_type_entry->id == TypeTableEntryIdFn) {
-//        return analyze_fn_call_ptr(g, import, context, expected_type, node, invoke_type_entry, nullptr);
-//    } else {
-//        add_node_error(g, fn_ref_expr,
-//            buf_sprintf("type '%s' not a function", buf_ptr(&invoke_type_entry->name)));
-//        return g->builtin_types.entry_invalid;
-//    }
-//}
 //static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
 //        TypeTableEntry *expected_type, AstNode *node)
 //{
@@ -7476,68 +7175,6 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
 //    return enum_type;
 //}
 //
-//static TypeTableEntry *analyze_container_member_access_inner(CodeGen *g,
-//    TypeTableEntry *bare_struct_type, Buf *field_name, AstNode *node, TypeTableEntry *struct_type)
-//{
-//    assert(node->type == NodeTypeFieldAccessExpr);
-//    if (!is_slice(bare_struct_type)) {
-//        BlockContext *container_block_context = get_container_block_context(bare_struct_type);
-//        assert(container_block_context);
-//        auto entry = container_block_context->decl_table.maybe_get(field_name);
-//        AstNode *fn_decl_node = entry ? entry->value : nullptr;
-//        if (fn_decl_node && fn_decl_node->type == NodeTypeFnProto) {
-//            resolve_top_level_decl(g, fn_decl_node, false);
-//            TopLevelDecl *tld = get_as_top_level_decl(fn_decl_node);
-//            if (tld->resolution == TldResolutionInvalid) {
-//                return g->builtin_types.entry_invalid;
-//            }
-//
-//            node->data.field_access_expr.is_member_fn = true;
-//            FnTableEntry *fn_entry = fn_decl_node->data.fn_proto.fn_table_entry;
-//            if (fn_entry->type_entry->id == TypeTableEntryIdGenericFn) {
-//                return resolve_expr_const_val_as_generic_fn(g, node, fn_entry->type_entry, false);
-//            } else {
-//                return resolve_expr_const_val_as_fn(g, node, fn_entry, false);
-//            }
-//        }
-//    }
-//    add_node_error(g, node,
-//        buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&bare_struct_type->name)));
-//    return g->builtin_types.entry_invalid;
-//}
-//
-//static TypeTableEntry *analyze_container_member_access(CodeGen *g,
-//        Buf *field_name, AstNode *node, TypeTableEntry *struct_type)
-//{
-//    TypeTableEntry *bare_type = container_ref_type(struct_type);
-//    if (!type_is_complete(bare_type)) {
-//        resolve_container_type(g, bare_type);
-//    }
-//
-//    node->data.field_access_expr.bare_container_type = bare_type;
-//
-//    if (bare_type->id == TypeTableEntryIdStruct) {
-//        node->data.field_access_expr.type_struct_field = find_struct_type_field(bare_type, field_name);
-//        if (node->data.field_access_expr.type_struct_field) {
-//            return node->data.field_access_expr.type_struct_field->type_entry;
-//        } else {
-//            return analyze_container_member_access_inner(g, bare_type, field_name,
-//                node, struct_type);
-//        }
-//    } else if (bare_type->id == TypeTableEntryIdEnum) {
-//        node->data.field_access_expr.type_enum_field = find_enum_type_field(bare_type, field_name);
-//        if (node->data.field_access_expr.type_enum_field) {
-//            return node->data.field_access_expr.type_enum_field->type_entry;
-//        } else {
-//            return analyze_container_member_access_inner(g, bare_type, field_name,
-//                node, struct_type);
-//        }
-//    } else if (bare_type->id == TypeTableEntryIdUnion) {
-//        zig_panic("TODO");
-//    } else {
-//        zig_unreachable();
-//    }
-//}
 //
 //static TypeTableEntry *analyze_slice_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
 //        AstNode *node)
@@ -7887,19 +7524,6 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
 //    return g->builtin_types.entry_invalid;
 //}
 //
-//static TypeTableEntry *analyze_fn_proto_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
-//        TypeTableEntry *expected_type, AstNode *node)
-//{
-//    TypeTableEntry *type_entry = analyze_fn_proto_type(g, import, context, expected_type, node,
-//            false, false, nullptr);
-//
-//    if (type_entry->id == TypeTableEntryIdInvalid) {
-//        return type_entry;
-//    }
-//
-//    return resolve_expr_const_val_as_type(g, node, type_entry, false);
-//}
-//
 //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"));
@@ -8239,92 +7863,6 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
 //    }
 //}
 //
-//static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {
-//    assert(node->type == NodeTypeFnCallExpr);
-//
-//    if (node->data.fn_call_expr.is_builtin) {
-//        return gen_builtin_fn_call_expr(g, node);
-//    }
-//
-//    FnTableEntry *fn_table_entry = node->data.fn_call_expr.fn_entry;
-//    TypeTableEntry *struct_type = nullptr;
-//    AstNode *first_param_expr = nullptr;
-//
-//    AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
-//    if (fn_ref_expr->type == NodeTypeFieldAccessExpr &&
-//        fn_ref_expr->data.field_access_expr.is_member_fn)
-//    {
-//        first_param_expr = fn_ref_expr->data.field_access_expr.struct_expr;
-//        struct_type = get_expr_type(first_param_expr);
-//    }
-//
-//    TypeTableEntry *fn_type;
-//    LLVMValueRef fn_val;
-//    AstNode *generic_proto_node;
-//    if (fn_table_entry) {
-//        fn_val = fn_table_entry->fn_value;
-//        fn_type = fn_table_entry->type_entry;
-//        generic_proto_node = fn_table_entry->proto_node->data.fn_proto.generic_proto_node;
-//    } else {
-//        fn_val = gen_expr(g, fn_ref_expr);
-//        fn_type = get_expr_type(fn_ref_expr);
-//        generic_proto_node = nullptr;
-//    }
-//
-//    TypeTableEntry *src_return_type = fn_type->data.fn.fn_type_id.return_type;
-//
-//    bool ret_has_bits = type_has_bits(src_return_type);
-//
-//    size_t fn_call_param_count = node->data.fn_call_expr.params.length;
-//    bool first_arg_ret = ret_has_bits && handle_is_ptr(src_return_type);
-//    size_t actual_param_count = fn_call_param_count + (struct_type ? 1 : 0) + (first_arg_ret ? 1 : 0);
-//    bool is_var_args = fn_type->data.fn.fn_type_id.is_var_args;
-//
-//    // don't really include void values
-//    LLVMValueRef *gen_param_values = allocate<LLVMValueRef>(actual_param_count);
-//
-//    size_t gen_param_index = 0;
-//    if (first_arg_ret) {
-//        gen_param_values[gen_param_index] = node->data.fn_call_expr.tmp_ptr;
-//        gen_param_index += 1;
-//    }
-//    if (struct_type && type_has_bits(struct_type)) {
-//        gen_param_values[gen_param_index] = gen_expr(g, first_param_expr);
-//        assert(gen_param_values[gen_param_index]);
-//        gen_param_index += 1;
-//    }
-//
-//    for (size_t call_i = 0; call_i < fn_call_param_count; call_i += 1) {
-//        size_t proto_i = call_i + (struct_type ? 1 : 0);
-//        if (generic_proto_node &&
-//            generic_proto_node->data.fn_proto.params.at(proto_i)->data.param_decl.is_inline)
-//        {
-//            continue;
-//        }
-//        AstNode *expr_node = node->data.fn_call_expr.params.at(call_i);
-//        LLVMValueRef param_value = gen_expr(g, expr_node);
-//        assert(param_value);
-//        TypeTableEntry *param_type = get_expr_type(expr_node);
-//        if (is_var_args || type_has_bits(param_type)) {
-//            gen_param_values[gen_param_index] = param_value;
-//            gen_param_index += 1;
-//        }
-//    }
-//
-//    LLVMValueRef result = ZigLLVMBuildCall(g->builder, fn_val,
-//            gen_param_values, gen_param_index, fn_type->data.fn.calling_convention, "");
-//
-//    if (src_return_type->id == TypeTableEntryIdUnreachable) {
-//        return LLVMBuildUnreachable(g->builder);
-//    } else if (!ret_has_bits) {
-//        return nullptr;
-//    } else if (first_arg_ret) {
-//        return node->data.fn_call_expr.tmp_ptr;
-//    } else {
-//        return result;
-//    }
-//}
-//
 //static LLVMValueRef gen_array_base_ptr(CodeGen *g, AstNode *node) {
 //    TypeTableEntry *type_entry = get_expr_type(node);
 //
@@ -8356,46 +7894,6 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
 //    return gen_array_elem_ptr(g, node, array_ptr, array_type, subscript_value);
 //}
 //
-//static LLVMValueRef gen_field_ptr(CodeGen *g, AstNode *node, TypeTableEntry **out_type_entry) {
-//    assert(node->type == NodeTypeFieldAccessExpr);
-//
-//    AstNode *struct_expr_node = node->data.field_access_expr.struct_expr;
-//
-//    *out_type_entry = node->data.field_access_expr.type_struct_field->type_entry;
-//    if (!type_has_bits(*out_type_entry)) {
-//        return nullptr;
-//    }
-//
-//    LLVMValueRef struct_ptr;
-//    if (struct_expr_node->type == NodeTypeSymbol) {
-//        VariableTableEntry *var = get_resolved_expr(struct_expr_node)->variable;
-//        assert(var);
-//
-//        if (var->type->id == TypeTableEntryIdPointer) {
-//            struct_ptr = LLVMBuildLoad(g->builder, var->value_ref, "");
-//        } else {
-//            struct_ptr = var->value_ref;
-//        }
-//    } else if (struct_expr_node->type == NodeTypeFieldAccessExpr) {
-//        struct_ptr = gen_field_access_expr(g, struct_expr_node, true);
-//        TypeTableEntry *field_type = get_expr_type(struct_expr_node);
-//        if (field_type->id == TypeTableEntryIdPointer) {
-//            // we have a double pointer so we must dereference it once
-//            struct_ptr = LLVMBuildLoad(g->builder, struct_ptr, "");
-//        }
-//    } else {
-//        struct_ptr = gen_expr(g, struct_expr_node);
-//    }
-//
-//    assert(LLVMGetTypeKind(LLVMTypeOf(struct_ptr)) == LLVMPointerTypeKind);
-//    assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(struct_ptr))) == LLVMStructTypeKind);
-//
-//    size_t gen_field_index = node->data.field_access_expr.type_struct_field->gen_index;
-//    assert(gen_field_index != SIZE_MAX);
-//
-//    return LLVMBuildStructGEP(g->builder, struct_ptr, gen_field_index, "");
-//}
-//
 //static LLVMValueRef gen_slice_expr(CodeGen *g, AstNode *node) {
 //    assert(node->type == NodeTypeSliceExpr);
 //
@@ -8771,87 +8269,6 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
 //    zig_unreachable();
 //}
 //
-//static LLVMValueRef gen_if_bool_expr_raw(CodeGen *g, AstNode *source_node, LLVMValueRef cond_value,
-//        AstNode *then_node, AstNode *else_node)
-//{
-//    assert(then_node);
-//    assert(else_node);
-//
-//    TypeTableEntry *then_type = get_expr_type(then_node);
-//    TypeTableEntry *else_type = get_expr_type(else_node);
-//
-//    bool use_then_value = type_has_bits(then_type);
-//    bool use_else_value = type_has_bits(else_type);
-//
-//    LLVMBasicBlockRef then_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Then");
-//    LLVMBasicBlockRef else_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Else");
-//
-//    LLVMBasicBlockRef endif_block = nullptr;
-//    bool then_endif_reachable = then_type->id != TypeTableEntryIdUnreachable;
-//    bool else_endif_reachable = else_type->id != TypeTableEntryIdUnreachable;
-//    if (then_endif_reachable || else_endif_reachable) {
-//        endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "EndIf");
-//    }
-//
-//    LLVMBuildCondBr(g->builder, cond_value, then_block, else_block);
-//
-//    LLVMPositionBuilderAtEnd(g->builder, then_block);
-//    LLVMValueRef then_expr_result = gen_expr(g, then_node);
-//    if (then_endif_reachable) {
-//        clear_debug_source_node(g);
-//        LLVMBuildBr(g->builder, endif_block);
-//    }
-//    LLVMBasicBlockRef after_then_block = LLVMGetInsertBlock(g->builder);
-//
-//    LLVMPositionBuilderAtEnd(g->builder, else_block);
-//    LLVMValueRef else_expr_result = gen_expr(g, else_node);
-//    if (else_endif_reachable) {
-//        clear_debug_source_node(g);
-//        LLVMBuildBr(g->builder, endif_block);
-//    }
-//    LLVMBasicBlockRef after_else_block = LLVMGetInsertBlock(g->builder);
-//
-//    if (then_endif_reachable || else_endif_reachable) {
-//        LLVMPositionBuilderAtEnd(g->builder, endif_block);
-//        if (use_then_value && use_else_value) {
-//            LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(then_expr_result), "");
-//            LLVMValueRef incoming_values[2] = {then_expr_result, else_expr_result};
-//            LLVMBasicBlockRef incoming_blocks[2] = {after_then_block, after_else_block};
-//            LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
-//            return phi;
-//        } else if (use_then_value) {
-//            return then_expr_result;
-//        } else if (use_else_value) {
-//            return else_expr_result;
-//        }
-//    }
-//
-//    return nullptr;
-//}
-//
-//static LLVMValueRef gen_if_bool_expr(CodeGen *g, AstNode *node) {
-//    assert(node->type == NodeTypeIfBoolExpr);
-//    assert(node->data.if_bool_expr.condition);
-//    assert(node->data.if_bool_expr.then_block);
-//
-//    ConstExprValue *const_val = &get_resolved_expr(node->data.if_bool_expr.condition)->const_val;
-//    if (const_val->ok) {
-//        if (const_val->data.x_bool) {
-//            return gen_expr(g, node->data.if_bool_expr.then_block);
-//        } else if (node->data.if_bool_expr.else_node) {
-//            return gen_expr(g, node->data.if_bool_expr.else_node);
-//        } else {
-//            return nullptr;
-//        }
-//    } else {
-//        LLVMValueRef cond_value = gen_expr(g, node->data.if_bool_expr.condition);
-//
-//        return gen_if_bool_expr_raw(g, node, cond_value,
-//                node->data.if_bool_expr.then_block,
-//                node->data.if_bool_expr.else_node);
-//    }
-//}
-//
 //static LLVMValueRef gen_block(CodeGen *g, AstNode *block_node, TypeTableEntry *implicit_return_type) {
 //    assert(block_node->type == NodeTypeBlock);
 //
@@ -8876,140 +8293,6 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
 //    }
 //}
 //
-//static LLVMValueRef gen_container_init_expr(CodeGen *g, AstNode *node) {
-//    assert(node->type == NodeTypeContainerInitExpr);
-//
-//    TypeTableEntry *type_entry = get_expr_type(node);
-//
-//
-//    if (node->data.container_init_expr.enum_type) {
-//        size_t param_count = node->data.container_init_expr.entries.length;
-//        AstNode *arg1_node;
-//        if (param_count == 1) {
-//            arg1_node = node->data.container_init_expr.entries.at(0);
-//        } else {
-//            assert(param_count == 0);
-//            arg1_node = nullptr;
-//        }
-//        return gen_enum_value_expr(g, node->data.container_init_expr.type,
-//                node->data.container_init_expr.enum_type, arg1_node);
-//    }
-//
-//
-//    if (type_entry->id == TypeTableEntryIdStruct) {
-//        assert(node->data.container_init_expr.kind == ContainerInitKindStruct);
-//
-//        size_t src_field_count = type_entry->data.structure.src_field_count;
-//        assert(src_field_count == node->data.container_init_expr.entries.length);
-//
-//        StructValExprCodeGen *struct_val_expr_node = &node->data.container_init_expr.resolved_struct_val_expr;
-//        LLVMValueRef tmp_struct_ptr = struct_val_expr_node->ptr;
-//
-//        for (size_t i = 0; i < src_field_count; i += 1) {
-//            AstNode *field_node = node->data.container_init_expr.entries.at(i);
-//            assert(field_node->type == NodeTypeStructValueField);
-//            TypeStructField *type_struct_field = field_node->data.struct_val_field.type_struct_field;
-//            if (type_struct_field->type_entry->id == TypeTableEntryIdVoid) {
-//                continue;
-//            }
-//            assert(buf_eql_buf(type_struct_field->name, field_node->data.struct_val_field.name));
-//
-//            set_debug_source_node(g, field_node);
-//            LLVMValueRef field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, type_struct_field->gen_index, "");
-//            AstNode *expr_node = field_node->data.struct_val_field.expr;
-//            LLVMValueRef value = gen_expr(g, expr_node);
-//            gen_assign_raw(g, field_node, BinOpTypeAssign, field_ptr, value,
-//                    type_struct_field->type_entry, get_expr_type(expr_node));
-//        }
-//
-//        return tmp_struct_ptr;
-//    } else if (type_entry->id == TypeTableEntryIdVoid) {
-//        assert(node->data.container_init_expr.entries.length == 0);
-//        return nullptr;
-//    } else if (type_entry->id == TypeTableEntryIdArray) {
-//        StructValExprCodeGen *struct_val_expr_node = &node->data.container_init_expr.resolved_struct_val_expr;
-//        LLVMValueRef tmp_array_ptr = struct_val_expr_node->ptr;
-//
-//        size_t field_count = type_entry->data.array.len;
-//        assert(field_count == node->data.container_init_expr.entries.length);
-//
-//        TypeTableEntry *child_type = type_entry->data.array.child_type;
-//
-//        for (size_t i = 0; i < field_count; i += 1) {
-//            AstNode *field_node = node->data.container_init_expr.entries.at(i);
-//            LLVMValueRef elem_val = gen_expr(g, field_node);
-//
-//            LLVMValueRef indices[] = {
-//                LLVMConstNull(g->builtin_types.entry_usize->type_ref),
-//                LLVMConstInt(g->builtin_types.entry_usize->type_ref, i, false),
-//            };
-//            set_debug_source_node(g, field_node);
-//            LLVMValueRef elem_ptr = LLVMBuildInBoundsGEP(g->builder, tmp_array_ptr, indices, 2, "");
-//            gen_assign_raw(g, field_node, BinOpTypeAssign, elem_ptr, elem_val,
-//                    child_type, get_expr_type(field_node));
-//        }
-//
-//        return tmp_array_ptr;
-//    } else {
-//        zig_unreachable();
-//    }
-//}
-//
-//static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) {
-//    assert(node->type == NodeTypeWhileExpr);
-//    assert(node->data.while_expr.condition);
-//    assert(node->data.while_expr.body);
-//
-//    //AstNode *continue_expr_node = node->data.while_expr.continue_expr;
-//
-//    bool condition_always_true = node->data.while_expr.condition_always_true;
-//    //bool contains_break = node->data.while_expr.contains_break;
-//    if (condition_always_true) {
-//        // generate a forever loop
-//        zig_panic("TODO IR");
-//
-//        //LLVMBasicBlockRef body_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileBody");
-//        //LLVMBasicBlockRef continue_block = continue_expr_node ?
-//        //    LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileContinue") : body_block;
-//        //LLVMBasicBlockRef end_block = nullptr;
-//        //if (contains_break) {
-//        //    end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileEnd");
-//        //}
-//
-//        //set_debug_source_node(g, node);
-//        //LLVMBuildBr(g->builder, body_block);
-//
-//        //if (continue_expr_node) {
-//        //    LLVMPositionBuilderAtEnd(g->builder, continue_block);
-//
-//        //    gen_expr(g, continue_expr_node);
-//
-//        //    set_debug_source_node(g, node);
-//        //    LLVMBuildBr(g->builder, body_block);
-//        //}
-//
-//        //LLVMPositionBuilderAtEnd(g->builder, body_block);
-//        //g->break_block_stack.append(end_block);
-//        //g->continue_block_stack.append(continue_block);
-//        //gen_expr(g, node->data.while_expr.body);
-//        //g->break_block_stack.pop();
-//        //g->continue_block_stack.pop();
-//
-//        //if (get_expr_type(node->data.while_expr.body)->id != TypeTableEntryIdUnreachable) {
-//        //    set_debug_source_node(g, node);
-//        //    LLVMBuildBr(g->builder, continue_block);
-//        //}
-//
-//        //if (contains_break) {
-//        //    LLVMPositionBuilderAtEnd(g->builder, end_block);
-//        //}
-//    } else {
-//        zig_panic("moved to ir.cpp");
-//    }
-//
-//    return nullptr;
-//}
-
 //static LLVMValueRef gen_break(CodeGen *g, AstNode *node) {
 //    assert(node->type == NodeTypeBreak);
 //    LLVMBasicBlockRef dest_block = g->break_block_stack.last();
@@ -9164,44 +8447,6 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
 //    }
 //}
 //
-//static LLVMValueRef gen_field_access_expr(CodeGen *g, AstNode *node, bool is_lvalue) {
-//    assert(node->type == NodeTypeFieldAccessExpr);
-//
-//    AstNode *struct_expr = node->data.field_access_expr.struct_expr;
-//    TypeTableEntry *struct_type = get_expr_type(struct_expr);
-//
-//    if (struct_type->id == TypeTableEntryIdArray) {
-//        Buf *name = node->data.field_access_expr.field_name;
-//        assert(buf_eql_str(name, "len"));
-//        return LLVMConstInt(g->builtin_types.entry_usize->type_ref,
-//                struct_type->data.array.len, false);
-//    } else if (struct_type->id == TypeTableEntryIdStruct || (struct_type->id == TypeTableEntryIdPointer &&
-//               struct_type->data.pointer.child_type->id == TypeTableEntryIdStruct))
-//    {
-//        TypeTableEntry *type_entry;
-//        LLVMValueRef ptr = gen_field_ptr(g, node, &type_entry);
-//        if (is_lvalue || handle_is_ptr(type_entry)) {
-//            return ptr;
-//        } else {
-//            return LLVMBuildLoad(g->builder, ptr, "");
-//        }
-//    } else if (struct_type->id == TypeTableEntryIdMetaType) {
-//        assert(!is_lvalue);
-//        TypeTableEntry *child_type = get_type_for_type_node(struct_expr);
-//        if (child_type->id == TypeTableEntryIdEnum) {
-//            return gen_enum_value_expr(g, node, child_type, nullptr);
-//        } else {
-//            zig_unreachable();
-//        }
-//    } else if (struct_type->id == TypeTableEntryIdNamespace) {
-//        VariableTableEntry *variable = get_resolved_expr(node)->variable;
-//        assert(variable);
-//        return gen_variable(g, node, variable);
-//    } else {
-//        zig_unreachable();
-//    }
-//}
-//
 //static LLVMValueRef gen_return(CodeGen *g, AstNode *source_node, LLVMValueRef value, ReturnKnowledge rk) {
 //    BlockContext *defer_inner_block = source_node->block_context;
 //    BlockContext *defer_outer_block = source_node->block_context->fn_entry->fn_def_node->block_context;
src/ir_print.cpp
@@ -7,6 +7,8 @@ struct IrPrint {
     int indent_size;
 };
 
+static void ir_print_other_instruction(IrPrint *irp, IrInstruction *instruction);
+
 static void ir_print_indent(IrPrint *irp) {
     for (int i = 0; i < irp->indent; i += 1) {
         fprintf(irp->f, " ");
@@ -35,25 +37,30 @@ static void ir_print_const_value(IrPrint *irp, TypeTableEntry *type_entry, Const
             break;
     }
     switch (type_entry->id) {
+        case TypeTableEntryIdTypeDecl:
+            return ir_print_const_value(irp, type_entry->data.type_decl.canonical_type, const_val);
         case TypeTableEntryIdInvalid:
             fprintf(irp->f, "(invalid)");
-            break;
+            return;
+        case TypeTableEntryIdVar:
+            fprintf(irp->f, "(var)");
+            return;
         case TypeTableEntryIdVoid:
             fprintf(irp->f, "{}");
-            break;
+            return;
         case TypeTableEntryIdNumLitFloat:
             fprintf(irp->f, "%f", const_val->data.x_bignum.data.x_float);
-            break;
+            return;
         case TypeTableEntryIdNumLitInt:
             {
                 BigNum *bignum = &const_val->data.x_bignum;
                 const char *negative_str = bignum->is_negative ? "-" : "";
                 fprintf(irp->f, "%s%llu", negative_str, bignum->data.x_uint);
-                break;
+                return;
             }
         case TypeTableEntryIdMetaType:
             fprintf(irp->f, "%s", buf_ptr(&const_val->data.x_type->name));
-            break;
+            return;
         case TypeTableEntryIdInt:
             {
                 BigNum *bignum = &const_val->data.x_bignum;
@@ -61,31 +68,38 @@ static void ir_print_const_value(IrPrint *irp, TypeTableEntry *type_entry, Const
                 const char *negative_str = bignum->is_negative ? "-" : "";
                 fprintf(irp->f, "%s%llu", negative_str, bignum->data.x_uint);
             }
-            break;
+            return;
+        case TypeTableEntryIdFloat:
+            {
+                BigNum *bignum = &const_val->data.x_bignum;
+                assert(bignum->kind == BigNumKindFloat);
+                fprintf(irp->f, "%f", bignum->data.x_float);
+            }
+            return;
         case TypeTableEntryIdUnreachable:
             fprintf(irp->f, "@unreachable()");
-            break;
+            return;
         case TypeTableEntryIdBool:
             {
                 const char *value = const_val->data.x_bool ? "true" : "false";
                 fprintf(irp->f, "%s", value);
-                break;
+                return;
             }
         case TypeTableEntryIdPointer:
             fprintf(irp->f, "&");
             ir_print_const_value(irp, type_entry->data.pointer.child_type, const_ptr_pointee(const_val));
-            break;
+            return;
         case TypeTableEntryIdFn:
             {
                 FnTableEntry *fn_entry = const_val->data.x_fn;
                 fprintf(irp->f, "%s", buf_ptr(&fn_entry->symbol_name));
-                break;
+                return;
             }
         case TypeTableEntryIdBlock:
             {
                 AstNode *node = const_val->data.x_block->node;
                 fprintf(irp->f, "(scope:%zu:%zu)", node->line + 1, node->column + 1);
-                break;
+                return;
             }
         case TypeTableEntryIdArray:
             {
@@ -99,12 +113,17 @@ static void ir_print_const_value(IrPrint *irp, TypeTableEntry *type_entry, Const
                     ir_print_const_value(irp, child_type, child_value);
                 }
                 fprintf(irp->f, "}");
-                break;
+                return;
             }
         case TypeTableEntryIdNullLit:
             {
                 fprintf(irp->f, "null");
-                break;
+                return;
+            }
+        case TypeTableEntryIdUndefLit:
+            {
+                fprintf(irp->f, "undefined");
+                return;
             }
         case TypeTableEntryIdMaybe:
             {
@@ -113,26 +132,56 @@ static void ir_print_const_value(IrPrint *irp, TypeTableEntry *type_entry, Const
                 } else {
                     fprintf(irp->f, "null");
                 }
-                break;
+                return;
             }
         case TypeTableEntryIdNamespace:
             {
                 ImportTableEntry *import = const_val->data.x_import;
                 fprintf(irp->f, "(namespace: %s)", buf_ptr(import->path));
-                break;
+                return;
+            }
+        case TypeTableEntryIdGenericFn:
+            {
+                TypeTableEntry *type_entry = const_val->data.x_type;
+                AstNode *decl_node = type_entry->data.generic_fn.decl_node;
+                assert(decl_node->type == NodeTypeFnProto);
+                fprintf(irp->f, "%s", buf_ptr(decl_node->data.fn_proto.name));
+                return;
+            }
+        case TypeTableEntryIdBoundFn:
+            {
+                FnTableEntry *fn_entry = const_val->data.x_bound_fn.fn;
+                fprintf(irp->f, "bound %s to ", buf_ptr(&fn_entry->symbol_name));
+                ir_print_other_instruction(irp, const_val->data.x_bound_fn.first_arg);
+                return;
             }
-        case TypeTableEntryIdVar:
-        case TypeTableEntryIdFloat:
         case TypeTableEntryIdStruct:
-        case TypeTableEntryIdUndefLit:
-        case TypeTableEntryIdErrorUnion:
-        case TypeTableEntryIdPureError:
+            {
+                fprintf(irp->f, "(struct %s constant)", buf_ptr(&type_entry->name));
+                return;
+            }
         case TypeTableEntryIdEnum:
+            {
+                fprintf(irp->f, "(enum %s constant)", buf_ptr(&type_entry->name));
+                return;
+            }
+        case TypeTableEntryIdErrorUnion:
+            {
+                fprintf(irp->f, "(error union %s constant)", buf_ptr(&type_entry->name));
+                return;
+            }
         case TypeTableEntryIdUnion:
-        case TypeTableEntryIdTypeDecl:
-        case TypeTableEntryIdGenericFn:
-            zig_panic("TODO render more constant types in IR printer");
+            {
+                fprintf(irp->f, "(union %s constant)", buf_ptr(&type_entry->name));
+                return;
+            }
+        case TypeTableEntryIdPureError:
+            {
+                fprintf(irp->f, "(pure error constant)");
+                return;
+            }
     }
+    zig_unreachable();
 }
 
 static void ir_print_const_instruction(IrPrint *irp, IrInstruction *instruction) {
@@ -285,12 +334,16 @@ static void ir_print_decl_var(IrPrint *irp, IrInstructionDeclVar *decl_var_instr
 static void ir_print_cast(IrPrint *irp, IrInstructionCast *cast_instruction) {
     fprintf(irp->f, "cast ");
     ir_print_other_instruction(irp, cast_instruction->value);
-    fprintf(irp->f, " to ");
-    ir_print_other_instruction(irp, cast_instruction->dest_type);
+    fprintf(irp->f, " to %s", buf_ptr(&cast_instruction->dest_type->name));
 }
 
 static void ir_print_call(IrPrint *irp, IrInstructionCall *call_instruction) {
-    ir_print_other_instruction(irp, call_instruction->fn);
+    if (call_instruction->fn_entry) {
+        fprintf(irp->f, "%s", buf_ptr(&call_instruction->fn_entry->symbol_name));
+    } else {
+        assert(call_instruction->fn_ref);
+        ir_print_other_instruction(irp, call_instruction->fn_ref);
+    }
     fprintf(irp->f, "(");
     for (size_t i = 0; i < call_instruction->arg_count; i += 1) {
         IrInstruction *arg = call_instruction->args[i];
@@ -347,13 +400,24 @@ static void ir_print_container_init_fields(IrPrint *irp, IrInstructionContainerI
     ir_print_other_instruction(irp, instruction->container_type);
     fprintf(irp->f, "{");
     for (size_t i = 0; i < instruction->field_count; i += 1) {
-        Buf *name = instruction->field_names[i];
-        IrInstruction *field_value = instruction->field_values[i];
+        IrInstructionContainerInitFieldsField *field = &instruction->fields[i];
         const char *comma = (i == 0) ? "" : ", ";
-        fprintf(irp->f, "%s.%s = ", comma, buf_ptr(name));
-        ir_print_other_instruction(irp, field_value);
+        fprintf(irp->f, "%s.%s = ", comma, buf_ptr(field->name));
+        ir_print_other_instruction(irp, field->value);
     }
-    fprintf(irp->f, "}");
+    fprintf(irp->f, "} // container init");
+}
+
+static void ir_print_struct_init(IrPrint *irp, IrInstructionStructInit *instruction) {
+    fprintf(irp->f, "%s {", buf_ptr(&instruction->struct_type->name));
+    for (size_t i = 0; i < instruction->field_count; i += 1) {
+        IrInstructionStructInitField *field = &instruction->fields[i];
+        Buf *field_name = field->type_struct_field->name;
+        const char *comma = (i == 0) ? "" : ", ";
+        fprintf(irp->f, "%s.%s = ", comma, buf_ptr(field_name));
+        ir_print_other_instruction(irp, field->value);
+    }
+    fprintf(irp->f, "} // struct init");
 }
 
 static void ir_print_unreachable(IrPrint *irp, IrInstructionUnreachable *instruction) {
@@ -406,10 +470,9 @@ static void ir_print_ptr_type_child(IrPrint *irp, IrInstructionPtrTypeChild *ins
 }
 
 static void ir_print_field_ptr(IrPrint *irp, IrInstructionFieldPtr *instruction) {
-    fprintf(irp->f, "@FieldPtr(&");
+    fprintf(irp->f, "fieldptr ");
     ir_print_other_instruction(irp, instruction->container_ptr);
     fprintf(irp->f, ".%s", buf_ptr(instruction->field_name));
-    fprintf(irp->f, ")");
 }
 
 static void ir_print_struct_field_ptr(IrPrint *irp, IrInstructionStructFieldPtr *instruction) {
@@ -419,6 +482,13 @@ static void ir_print_struct_field_ptr(IrPrint *irp, IrInstructionStructFieldPtr
     fprintf(irp->f, ")");
 }
 
+static void ir_print_enum_field_ptr(IrPrint *irp, IrInstructionEnumFieldPtr *instruction) {
+    fprintf(irp->f, "@EnumFieldPtr(&");
+    ir_print_other_instruction(irp, instruction->enum_ptr);
+    fprintf(irp->f, ".%s", buf_ptr(instruction->field->name));
+    fprintf(irp->f, ")");
+}
+
 static void ir_print_set_fn_test(IrPrint *irp, IrInstructionSetFnTest *instruction) {
     fprintf(irp->f, "@setFnTest(");
     ir_print_other_instruction(irp, instruction->fn_value);
@@ -632,6 +702,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
         case IrInstructionIdContainerInitFields:
             ir_print_container_init_fields(irp, (IrInstructionContainerInitFields *)instruction);
             break;
+        case IrInstructionIdStructInit:
+            ir_print_struct_init(irp, (IrInstructionStructInit *)instruction);
+            break;
         case IrInstructionIdUnreachable:
             ir_print_unreachable(irp, (IrInstructionUnreachable *)instruction);
             break;
@@ -662,6 +735,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
         case IrInstructionIdStructFieldPtr:
             ir_print_struct_field_ptr(irp, (IrInstructionStructFieldPtr *)instruction);
             break;
+        case IrInstructionIdEnumFieldPtr:
+            ir_print_enum_field_ptr(irp, (IrInstructionEnumFieldPtr *)instruction);
+            break;
         case IrInstructionIdSetFnTest:
             ir_print_set_fn_test(irp, (IrInstructionSetFnTest *)instruction);
             break;
test/self_hosted2.zig
@@ -84,6 +84,18 @@ end:
 }
 var goto_counter: i32 = 0;
 
+
+
+struct FooA {
+    fn add(a: i32, b: i32) -> i32 { a + b }
+}
+const foo_a = FooA {};
+
+fn testStructStatic() {
+    const result = FooA.add(3, 4);
+    assert(result == 7);
+}
+
 fn assert(ok: bool) {
     if (!ok)
         @unreachable();
@@ -98,6 +110,7 @@ fn runAllTests() {
     testInlineSwitch();
     testNamespaceFnCall();
     gotoAndLabels();
+    testStructStatic();
 }
 
 export nakedcc fn _start() -> unreachable {