Commit 77ae3442ef

Andrew Kelley <superjoe30@gmail.com>
2016-10-09 08:20:01
explicit casting works with IR
1 parent 07fe60d
src/all_types.hpp
@@ -29,6 +29,7 @@ struct TypeStructField;
 struct CodeGen;
 struct ConstExprValue;
 struct IrInstruction;
+struct IrInstructionCast;
 struct IrBasicBlock;
 
 struct IrExecutable {
@@ -1121,7 +1122,7 @@ struct FnTableEntry {
     AstNode *fn_test_set_node;
     AstNode *fn_static_eval_set_node;
 
-    ZigList<AstNode *> cast_alloca_list;
+    ZigList<IrInstructionCast *> cast_alloca_list;
     ZigList<StructValExprCodeGen *> struct_val_expr_alloca_list;
     ZigList<VariableTableEntry *> variable_list;
     ZigList<AstNode *> goto_list;
@@ -1435,6 +1436,7 @@ struct IrInstruction {
     // if ref_count is zero, instruction can be omitted in codegen
     size_t ref_count;
     IrInstruction *other;
+    ReturnKnowledge return_knowledge;
 };
 
 struct IrInstructionCondBr {
@@ -1547,7 +1549,8 @@ struct IrInstructionCast {
 
     IrInstruction *value;
     IrInstruction *dest_type;
-    bool is_implicit;
+    CastOp cast_op;
+    LLVMValueRef tmp_ptr;
 };
 
 #endif
src/analyze.cpp
@@ -25,8 +25,6 @@ static VariableTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableE
         BlockContext *context, TypeTableEntry *expected_type, AstNode *node);
 static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableEntry *struct_type);
 static TypeTableEntry *unwrapped_node_type(AstNode *node);
-static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
-        AstNode *node);
 static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry *import,
         BlockContext *context, AstNode *node, Buf *err_name);
 static TypeTableEntry *analyze_block_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
@@ -115,7 +113,7 @@ AstNode *first_executing_node(AstNode *node) {
     zig_unreachable();
 }
 
-static void mark_impure_fn(CodeGen *g, BlockContext *context, AstNode *node) {
+void mark_impure_fn(CodeGen *g, BlockContext *context, AstNode *node) {
     if (!context->fn_entry) return;
     if (!context->fn_entry->is_pure) return;
 
@@ -261,11 +259,6 @@ uint64_t type_size(CodeGen *g, TypeTableEntry *type_entry) {
     }
 }
 
-static bool is_u8(TypeTableEntry *type) {
-    return type->id == TypeTableEntryIdInt &&
-        !type->data.integral.is_signed && type->data.integral.bit_count == 8;
-}
-
 static bool is_slice(TypeTableEntry *type) {
     return type->id == TypeTableEntryIdStruct && type->data.structure.is_slice;
 }
@@ -4466,30 +4459,7 @@ static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *impor
     }
 }
 
-static TypeTableEntry *resolve_cast(CodeGen *g, BlockContext *context, AstNode *node,
-        AstNode *expr_node, TypeTableEntry *wanted_type, CastOp op, bool need_alloca)
-{
-    node->data.fn_call_expr.cast_op = op;
-
-    ConstExprValue *other_val = &get_resolved_expr(expr_node)->const_val;
-    TypeTableEntry *other_type = get_resolved_expr(expr_node)->type_entry;
-    ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
-    if (other_val->ok) {
-        eval_const_expr_implicit_cast(node->data.fn_call_expr.cast_op, other_val, other_type,
-                const_val, wanted_type);
-    }
-
-    if (need_alloca) {
-        if (context->fn_entry) {
-            context->fn_entry->cast_alloca_list.append(node);
-        } else {
-            assert(get_resolved_expr(node)->const_val.ok);
-        }
-    }
-    return wanted_type;
-}
-
-static bool type_is_codegen_pointer(TypeTableEntry *type) {
+bool type_is_codegen_pointer(TypeTableEntry *type) {
     if (type->id == TypeTableEntryIdPointer) return true;
     if (type->id == TypeTableEntryIdFn) return true;
     if (type->id == TypeTableEntryIdMaybe) {
@@ -4499,256 +4469,6 @@ static bool type_is_codegen_pointer(TypeTableEntry *type) {
     return false;
 }
 
-static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
-        AstNode *node)
-{
-    assert(node->type == NodeTypeFnCallExpr);
-
-    AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
-    size_t actual_param_count = node->data.fn_call_expr.params.length;
-
-    if (actual_param_count != 1) {
-        add_node_error(g, fn_ref_expr, buf_sprintf("cast expression expects exactly one parameter"));
-        return g->builtin_types.entry_invalid;
-    }
-
-    AstNode *expr_node = node->data.fn_call_expr.params.at(0);
-    TypeTableEntry *wanted_type = resolve_type(g, fn_ref_expr);
-    TypeTableEntry *actual_type = analyze_expression(g, import, context, nullptr, expr_node);
-    TypeTableEntry *wanted_type_canon = get_underlying_type(wanted_type);
-    TypeTableEntry *actual_type_canon = get_underlying_type(actual_type);
-
-    if (wanted_type_canon->id == TypeTableEntryIdInvalid ||
-        actual_type_canon->id == TypeTableEntryIdInvalid)
-    {
-        return g->builtin_types.entry_invalid;
-    }
-
-    // explicit match or non-const to const
-    if (types_match_const_cast_only(wanted_type, actual_type)) {
-        return resolve_cast(g, context, node, expr_node, wanted_type, CastOpNoop, false);
-    }
-
-    // explicit cast from bool to int
-    if (wanted_type_canon->id == TypeTableEntryIdInt &&
-        actual_type_canon->id == TypeTableEntryIdBool)
-    {
-        return resolve_cast(g, context, node, expr_node, wanted_type, CastOpBoolToInt, false);
-    }
-
-    // explicit cast from pointer to isize or usize
-    if ((wanted_type_canon == g->builtin_types.entry_isize || wanted_type_canon == g->builtin_types.entry_usize) &&
-        type_is_codegen_pointer(actual_type_canon))
-    {
-        return resolve_cast(g, context, node, expr_node, wanted_type, CastOpPtrToInt, false);
-    }
-
-
-    // explicit cast from isize or usize to pointer
-    if (wanted_type_canon->id == TypeTableEntryIdPointer &&
-        (actual_type_canon == g->builtin_types.entry_isize || actual_type_canon == g->builtin_types.entry_usize))
-    {
-        return resolve_cast(g, context, node, expr_node, wanted_type, CastOpIntToPtr, false);
-    }
-
-    // explicit widening or shortening cast
-    if ((wanted_type_canon->id == TypeTableEntryIdInt &&
-        actual_type_canon->id == TypeTableEntryIdInt) ||
-        (wanted_type_canon->id == TypeTableEntryIdFloat &&
-        actual_type_canon->id == TypeTableEntryIdFloat))
-    {
-        return resolve_cast(g, context, node, expr_node, wanted_type, CastOpWidenOrShorten, false);
-    }
-
-    // explicit cast from int to float
-    if (wanted_type_canon->id == TypeTableEntryIdFloat &&
-        actual_type_canon->id == TypeTableEntryIdInt)
-    {
-        return resolve_cast(g, context, node, expr_node, wanted_type, CastOpIntToFloat, false);
-    }
-
-    // explicit cast from float to int
-    if (wanted_type_canon->id == TypeTableEntryIdInt &&
-        actual_type_canon->id == TypeTableEntryIdFloat)
-    {
-        return resolve_cast(g, context, node, expr_node, wanted_type, CastOpFloatToInt, false);
-    }
-
-    // explicit cast from array to slice
-    if (is_slice(wanted_type) &&
-        actual_type->id == TypeTableEntryIdArray &&
-        types_match_const_cast_only(
-            wanted_type->data.structure.fields[0].type_entry->data.pointer.child_type,
-            actual_type->data.array.child_type))
-    {
-        return resolve_cast(g, context, node, expr_node, wanted_type, CastOpToUnknownSizeArray, true);
-    }
-
-    // explicit cast from []T to []u8 or []u8 to []T
-    if (is_slice(wanted_type) && is_slice(actual_type) &&
-        (is_u8(wanted_type->data.structure.fields[0].type_entry->data.pointer.child_type) ||
-        is_u8(actual_type->data.structure.fields[0].type_entry->data.pointer.child_type)) &&
-        (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(g, context, node);
-        return resolve_cast(g, context, node, expr_node, wanted_type, CastOpResizeSlice, true);
-    }
-
-    // explicit cast from [N]u8 to []T
-    if (is_slice(wanted_type) &&
-        actual_type->id == TypeTableEntryIdArray &&
-        is_u8(actual_type->data.array.child_type))
-    {
-        mark_impure_fn(g, context, node);
-        uint64_t child_type_size = type_size(g,
-                wanted_type->data.structure.fields[0].type_entry->data.pointer.child_type);
-        if (actual_type->data.array.len % child_type_size == 0) {
-            return resolve_cast(g, context, node, expr_node, wanted_type, CastOpBytesToSlice, true);
-        } else {
-            add_node_error(g, node,
-                    buf_sprintf("unable to convert %s to %s: size mismatch",
-                        buf_ptr(&actual_type->name), buf_ptr(&wanted_type->name)));
-            return g->builtin_types.entry_invalid;
-        }
-    }
-
-    // explicit cast from pointer to another pointer
-    if ((actual_type->id == TypeTableEntryIdPointer || actual_type->id == TypeTableEntryIdFn) &&
-        (wanted_type->id == TypeTableEntryIdPointer || wanted_type->id == TypeTableEntryIdFn))
-    {
-        return resolve_cast(g, context, node, expr_node, wanted_type, CastOpPointerReinterpret, false);
-    }
-
-    // explicit cast from maybe pointer to another maybe pointer
-    if (actual_type->id == TypeTableEntryIdMaybe &&
-        (actual_type->data.maybe.child_type->id == TypeTableEntryIdPointer ||
-            actual_type->data.maybe.child_type->id == TypeTableEntryIdFn) &&
-        wanted_type->id == TypeTableEntryIdMaybe &&
-        (wanted_type->data.maybe.child_type->id == TypeTableEntryIdPointer ||
-            wanted_type->data.maybe.child_type->id == TypeTableEntryIdFn))
-    {
-        return resolve_cast(g, context, node, expr_node, 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)) {
-            get_resolved_expr(node)->return_knowledge = ReturnKnowledgeKnownNonNull;
-            return resolve_cast(g, context, node, expr_node, wanted_type, CastOpMaybeWrap, true);
-        } else if (actual_type->id == TypeTableEntryIdNumLitInt ||
-                   actual_type->id == TypeTableEntryIdNumLitFloat)
-        {
-            if (num_lit_fits_in_other_type(g, expr_node, wanted_type->data.maybe.child_type)) {
-                get_resolved_expr(node)->return_knowledge = ReturnKnowledgeKnownNonNull;
-                return resolve_cast(g, context, node, expr_node, wanted_type, CastOpMaybeWrap, true);
-            } else {
-                return g->builtin_types.entry_invalid;
-            }
-        }
-    }
-
-    // explicit cast from null literal to maybe type
-    if (wanted_type->id == TypeTableEntryIdMaybe &&
-        actual_type->id == TypeTableEntryIdNullLit)
-    {
-        get_resolved_expr(node)->return_knowledge = ReturnKnowledgeKnownNull;
-        return resolve_cast(g, context, node, expr_node, wanted_type, CastOpNullToMaybe, true);
-    }
-
-    // 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)) {
-            get_resolved_expr(node)->return_knowledge = ReturnKnowledgeKnownNonError;
-            return resolve_cast(g, context, node, expr_node, wanted_type, CastOpErrorWrap, true);
-        } else if (actual_type->id == TypeTableEntryIdNumLitInt ||
-                   actual_type->id == TypeTableEntryIdNumLitFloat)
-        {
-            if (num_lit_fits_in_other_type(g, expr_node, wanted_type->data.error.child_type)) {
-                get_resolved_expr(node)->return_knowledge = ReturnKnowledgeKnownNonError;
-                return resolve_cast(g, context, node, expr_node, wanted_type, CastOpErrorWrap, true);
-            } else {
-                return g->builtin_types.entry_invalid;
-            }
-        }
-    }
-
-    // explicit cast from pure error to error union type
-    if (wanted_type->id == TypeTableEntryIdErrorUnion &&
-        actual_type->id == TypeTableEntryIdPureError)
-    {
-        get_resolved_expr(node)->return_knowledge = ReturnKnowledgeKnownError;
-        return resolve_cast(g, context, node, expr_node, wanted_type, CastOpPureErrorWrap, false);
-    }
-
-    // explicit cast from number literal to another type
-    if (actual_type->id == TypeTableEntryIdNumLitFloat ||
-        actual_type->id == TypeTableEntryIdNumLitInt)
-    {
-        if (num_lit_fits_in_other_type(g, expr_node, wanted_type_canon)) {
-            CastOp op;
-            if ((actual_type->id == TypeTableEntryIdNumLitFloat &&
-                 wanted_type_canon->id == TypeTableEntryIdFloat) ||
-                (actual_type->id == TypeTableEntryIdNumLitInt &&
-                 wanted_type_canon->id == TypeTableEntryIdInt))
-            {
-                op = CastOpNoop;
-            } else if (wanted_type_canon->id == TypeTableEntryIdInt) {
-                op = CastOpFloatToInt;
-            } else if (wanted_type_canon->id == TypeTableEntryIdFloat) {
-                op = CastOpIntToFloat;
-            } else {
-                zig_unreachable();
-            }
-            return resolve_cast(g, context, node, expr_node, wanted_type, op, false);
-        } else {
-            return g->builtin_types.entry_invalid;
-        }
-    }
-
-    // explicit cast from %void to integer type which can fit it
-    bool actual_type_is_void_err = actual_type->id == TypeTableEntryIdErrorUnion &&
-        !type_has_bits(actual_type->data.error.child_type);
-    bool actual_type_is_pure_err = actual_type->id == TypeTableEntryIdPureError;
-    if ((actual_type_is_void_err || actual_type_is_pure_err) &&
-        wanted_type->id == TypeTableEntryIdInt)
-    {
-        BigNum bn;
-        bignum_init_unsigned(&bn, g->error_decls.length);
-        if (bignum_fits_in_bits(&bn, wanted_type->data.integral.bit_count,
-                    wanted_type->data.integral.is_signed))
-        {
-            return resolve_cast(g, context, node, expr_node, wanted_type, CastOpErrToInt, false);
-        } else {
-            add_node_error(g, node,
-                    buf_sprintf("too many error values to fit in '%s'", buf_ptr(&wanted_type->name)));
-            return g->builtin_types.entry_invalid;
-        }
-    }
-
-    // explicit cast from integer to enum type with no payload
-    if (actual_type->id == TypeTableEntryIdInt &&
-        wanted_type->id == TypeTableEntryIdEnum &&
-        wanted_type->data.enumeration.gen_field_count == 0)
-    {
-        return resolve_cast(g, context, node, expr_node, wanted_type, CastOpIntToEnum, false);
-    }
-
-    // explicit cast from enum type with no payload to integer
-    if (wanted_type->id == TypeTableEntryIdInt &&
-        actual_type->id == TypeTableEntryIdEnum &&
-        actual_type->data.enumeration.gen_field_count == 0)
-    {
-        return resolve_cast(g, context, node, expr_node, wanted_type, CastOpEnumToInt, false);
-    }
-
-    add_node_error(g, node,
-        buf_sprintf("invalid cast from type '%s' to '%s'",
-            buf_ptr(&actual_type->name),
-            buf_ptr(&wanted_type->name)));
-    return g->builtin_types.entry_invalid;
-}
-
 static TypeTableEntry *analyze_import(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         AstNode *node)
 {
@@ -5866,13 +5586,14 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import,
         }
     }
 
-    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"));
-        }
-    }
+    // 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;
 }
@@ -6110,7 +5831,7 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import
 
     if (const_val->ok) {
         if (invoke_type_entry->id == TypeTableEntryIdMetaType) {
-            return analyze_cast_expr(g, import, context, node);
+            zig_unreachable();
         } else if (invoke_type_entry->id == TypeTableEntryIdFn) {
             AstNode *struct_node;
             if (fn_ref_expr->type == NodeTypeFieldAccessExpr &&
src/analyze.hpp
@@ -49,10 +49,14 @@ TypeTableEntry *resolve_peer_type_compatibility(CodeGen *g, ImportTableEntry *im
         BlockContext *block_context, AstNode *parent_source_node,
         AstNode **child_nodes, TypeTableEntry **child_types, size_t child_count);
 
+
+
 bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry *actual_type);
 VariableTableEntry *find_variable(CodeGen *g, BlockContext *orig_context, Buf *name);
 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);
 
 #endif
src/codegen.cpp
@@ -344,11 +344,10 @@ static LLVMValueRef get_int_builtin_fn(CodeGen *g, TypeTableEntry *int_type, Bui
     return *fn;
 }
 
-static LLVMValueRef get_handle_value(CodeGen *g, AstNode *source_node, LLVMValueRef ptr, TypeTableEntry *type) {
+static LLVMValueRef get_handle_value(CodeGen *g, LLVMValueRef ptr, TypeTableEntry *type) {
     if (handle_is_ptr(type)) {
         return ptr;
     } else {
-        set_debug_source_node(g, source_node);
         return LLVMBuildLoad(g->builder, ptr, "");
     }
 }
@@ -378,7 +377,7 @@ static void gen_debug_safety_crash(CodeGen *g) {
     LLVMBuildUnreachable(g->builder);
 }
 
-static void add_bounds_check(CodeGen *g, AstNode *source_node, LLVMValueRef target_val,
+static void add_bounds_check(CodeGen *g, LLVMValueRef target_val,
         LLVMIntPredicate lower_pred, LLVMValueRef lower_value,
         LLVMIntPredicate upper_pred, LLVMValueRef upper_value)
 {
@@ -391,8 +390,6 @@ static void add_bounds_check(CodeGen *g, AstNode *source_node, LLVMValueRef targ
         upper_value = nullptr;
     }
 
-    set_debug_source_node(g, source_node);
-
     LLVMBasicBlockRef bounds_check_fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoundsCheckFail");
     LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoundsCheckOk");
     LLVMBasicBlockRef lower_ok_block = upper_value ?
@@ -425,12 +422,11 @@ static LLVMValueRef gen_err_name(CodeGen *g, AstNode *node) {
 
     AstNode *err_val_node = node->data.fn_call_expr.params.at(0);
     LLVMValueRef err_val = gen_expr(g, err_val_node);
-    set_debug_source_node(g, node);
 
     if (want_debug_safety(g, node)) {
         LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(err_val));
         LLVMValueRef end_val = LLVMConstInt(LLVMTypeOf(err_val), g->error_decls.length, false);
-        add_bounds_check(g, node, err_val, LLVMIntNE, zero, LLVMIntULT, end_val);
+        add_bounds_check(g, err_val, LLVMIntNE, zero, LLVMIntULT, end_val);
     }
 
     LLVMValueRef indices[] = {
@@ -514,15 +510,12 @@ static LLVMValueRef gen_truncate(CodeGen *g, AstNode *node) {
 
     LLVMValueRef src_val = gen_expr(g, src_node);
 
-    set_debug_source_node(g, node);
     return LLVMBuildTrunc(g->builder, src_val, dest_type->type_ref, "");
 }
 
 static LLVMValueRef gen_unreachable(CodeGen *g, AstNode *node) {
     assert(node->type == NodeTypeFnCallExpr);
 
-    set_debug_source_node(g, node);
-
     if (want_debug_safety(g, node) || g->is_test_build) {
         gen_debug_safety_crash(g);
     } else {
@@ -545,7 +538,6 @@ static LLVMValueRef gen_shl_with_overflow(CodeGen *g, AstNode *node) {
     LLVMValueRef val2 = gen_expr(g, node->data.fn_call_expr.params.at(2));
     LLVMValueRef ptr_result = gen_expr(g, node->data.fn_call_expr.params.at(3));
 
-    set_debug_source_node(g, node);
     LLVMValueRef result = LLVMBuildShl(g->builder, val1, val2, "");
     LLVMValueRef orig_val;
     if (int_type->data.integral.is_signed) {
@@ -590,7 +582,6 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
                     operand,
                     LLVMConstNull(LLVMInt1Type()),
                 };
-                set_debug_source_node(g, node);
                 return LLVMBuildCall(g->builder, fn_val, params, 2, "");
             }
         case BuiltinFnIdAddWithOverflow:
@@ -622,7 +613,6 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
                     op2,
                 };
 
-                set_debug_source_node(g, node);
                 LLVMValueRef result_struct = LLVMBuildCall(g->builder, fn_val, params, 2, "");
                 LLVMValueRef result = LLVMBuildExtractValue(g->builder, result_struct, 0, "");
                 LLVMValueRef overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 1, "");
@@ -646,7 +636,6 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
 
                 LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0);
 
-                set_debug_source_node(g, node);
                 LLVMValueRef dest_ptr_casted = LLVMBuildBitCast(g->builder, dest_ptr, ptr_u8, "");
                 LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, src_ptr, ptr_u8, "");
 
@@ -677,7 +666,6 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
 
                 LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0);
 
-                set_debug_source_node(g, node);
                 LLVMValueRef dest_ptr_casted = LLVMBuildBitCast(g->builder, dest_ptr, ptr_u8, "");
 
                 uint64_t align_in_bytes = get_memcpy_align(g, dest_type->data.pointer.child_type);
@@ -707,13 +695,11 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
         case BuiltinFnIdErrName:
             return gen_err_name(g, node);
         case BuiltinFnIdBreakpoint:
-            set_debug_source_node(g, node);
             return LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, "");
         case BuiltinFnIdFrameAddress:
         case BuiltinFnIdReturnAddress:
             {
                 LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_i32->type_ref);
-                set_debug_source_node(g, node);
                 return LLVMBuildCall(g->builder, builtin_fn->fn_val, &zero, 1, "");
             }
         case BuiltinFnIdCmpExchange:
@@ -760,7 +746,6 @@ static LLVMValueRef gen_enum_value_expr(CodeGen *g, AstNode *node, TypeTableEntr
         LLVMValueRef tmp_struct_ptr = node->data.field_access_expr.resolved_struct_val_expr.ptr;
 
         // populate the new tag value
-        set_debug_source_node(g, node);
         LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 0, "");
         LLVMBuildStore(g->builder, tag_value, tag_field_ptr);
 
@@ -804,7 +789,6 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, AstNode *source_node, TypeT
         !wanted_type->data.integral.is_signed && actual_type->data.integral.is_signed &&
         want_debug_safety(g, source_node))
     {
-        set_debug_source_node(g, source_node);
         LLVMValueRef zero = LLVMConstNull(actual_type->type_ref);
         LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntSGE, expr_val, zero, "");
 
@@ -822,14 +806,11 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, AstNode *source_node, TypeT
         return expr_val;
     } else if (actual_bits < wanted_bits) {
         if (actual_type->id == TypeTableEntryIdFloat) {
-            set_debug_source_node(g, source_node);
             return LLVMBuildFPExt(g->builder, expr_val, wanted_type->type_ref, "");
         } else if (actual_type->id == TypeTableEntryIdInt) {
             if (actual_type->data.integral.is_signed) {
-                set_debug_source_node(g, source_node);
                 return LLVMBuildSExt(g->builder, expr_val, wanted_type->type_ref, "");
             } else {
-                set_debug_source_node(g, source_node);
                 return LLVMBuildZExt(g->builder, expr_val, wanted_type->type_ref, "");
             }
         } else {
@@ -837,10 +818,8 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, AstNode *source_node, TypeT
         }
     } else if (actual_bits > wanted_bits) {
         if (actual_type->id == TypeTableEntryIdFloat) {
-            set_debug_source_node(g, source_node);
             return LLVMBuildFPTrunc(g->builder, expr_val, wanted_type->type_ref, "");
         } else if (actual_type->id == TypeTableEntryIdInt) {
-            set_debug_source_node(g, source_node);
             LLVMValueRef trunc_val = LLVMBuildTrunc(g->builder, expr_val, wanted_type->type_ref, "");
             if (!want_debug_safety(g, source_node)) {
                 return trunc_val;
@@ -869,256 +848,11 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, AstNode *source_node, TypeT
     }
 }
 
-static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) {
-    assert(node->type == NodeTypeFnCallExpr);
-
-    AstNode *expr_node = node->data.fn_call_expr.params.at(0);
-
-    LLVMValueRef expr_val = gen_expr(g, expr_node);
-
-    TypeTableEntry *actual_type = get_expr_type(expr_node);
-    TypeTableEntry *wanted_type = get_expr_type(node);
-
-    AstNodeFnCallExpr *cast_expr = &node->data.fn_call_expr;
-
-    switch (cast_expr->cast_op) {
-        case CastOpNoCast:
-            zig_unreachable();
-        case CastOpNoop:
-            return expr_val;
-        case CastOpErrToInt:
-            assert(actual_type->id == TypeTableEntryIdErrorUnion);
-            if (!type_has_bits(actual_type->data.error.child_type)) {
-                return gen_widen_or_shorten(g, node, g->err_tag_type, wanted_type, expr_val);
-            } else {
-                zig_panic("TODO");
-            }
-        case CastOpMaybeWrap:
-            {
-                assert(cast_expr->tmp_ptr);
-                assert(wanted_type->id == TypeTableEntryIdMaybe);
-                assert(actual_type);
-
-                TypeTableEntry *child_type = wanted_type->data.maybe.child_type;
-
-                if (child_type->id == TypeTableEntryIdPointer ||
-                    child_type->id == TypeTableEntryIdFn)
-                {
-                    return expr_val;
-                } else {
-                    set_debug_source_node(g, node);
-                    LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, 0, "");
-                    gen_assign_raw(g, node, BinOpTypeAssign,
-                            val_ptr, expr_val, child_type, actual_type);
-
-                    set_debug_source_node(g, node);
-                    LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, 1, "");
-                    LLVMBuildStore(g->builder, LLVMConstAllOnes(LLVMInt1Type()), maybe_ptr);
-                }
-
-                return cast_expr->tmp_ptr;
-            }
-        case CastOpNullToMaybe:
-            // handled by constant expression evaluator
-            zig_unreachable();
-        case CastOpErrorWrap:
-            {
-                assert(wanted_type->id == TypeTableEntryIdErrorUnion);
-                TypeTableEntry *child_type = wanted_type->data.error.child_type;
-                LLVMValueRef ok_err_val = LLVMConstNull(g->err_tag_type->type_ref);
-
-                if (!type_has_bits(child_type)) {
-                    return ok_err_val;
-                } else {
-                    assert(cast_expr->tmp_ptr);
-                    assert(wanted_type->id == TypeTableEntryIdErrorUnion);
-                    assert(actual_type);
-
-                    set_debug_source_node(g, node);
-                    LLVMValueRef err_tag_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, 0, "");
-                    LLVMBuildStore(g->builder, ok_err_val, err_tag_ptr);
-
-                    LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, 1, "");
-                    gen_assign_raw(g, node, BinOpTypeAssign,
-                            payload_ptr, expr_val, child_type, actual_type);
-
-                    return cast_expr->tmp_ptr;
-                }
-            }
-        case CastOpPureErrorWrap:
-            assert(wanted_type->id == TypeTableEntryIdErrorUnion);
-
-            if (!type_has_bits(wanted_type->data.error.child_type)) {
-                return expr_val;
-            } else {
-                zig_panic("TODO");
-            }
-        case CastOpPtrToInt:
-            set_debug_source_node(g, node);
-            return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, "");
-        case CastOpIntToPtr:
-            set_debug_source_node(g, node);
-            return LLVMBuildIntToPtr(g->builder, expr_val, wanted_type->type_ref, "");
-        case CastOpPointerReinterpret:
-            set_debug_source_node(g, node);
-            return LLVMBuildBitCast(g->builder, expr_val, wanted_type->type_ref, "");
-        case CastOpWidenOrShorten:
-            return gen_widen_or_shorten(g, node, actual_type, wanted_type, expr_val);
-        case CastOpToUnknownSizeArray:
-            {
-                assert(cast_expr->tmp_ptr);
-                assert(wanted_type->id == TypeTableEntryIdStruct);
-                assert(wanted_type->data.structure.is_slice);
-
-                TypeTableEntry *pointer_type = wanted_type->data.structure.fields[0].type_entry;
-
-                set_debug_source_node(g, node);
-
-                size_t ptr_index = wanted_type->data.structure.fields[0].gen_index;
-                if (ptr_index != SIZE_MAX) {
-                    LLVMValueRef ptr_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, ptr_index, "");
-                    LLVMValueRef expr_bitcast = LLVMBuildBitCast(g->builder, expr_val, pointer_type->type_ref, "");
-                    LLVMBuildStore(g->builder, expr_bitcast, ptr_ptr);
-                }
-
-                size_t len_index = wanted_type->data.structure.fields[1].gen_index;
-                LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, len_index, "");
-                LLVMValueRef len_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref,
-                        actual_type->data.array.len, false);
-                LLVMBuildStore(g->builder, len_val, len_ptr);
-
-                return cast_expr->tmp_ptr;
-            }
-        case CastOpResizeSlice:
-            {
-                assert(cast_expr->tmp_ptr);
-                assert(wanted_type->id == TypeTableEntryIdStruct);
-                assert(wanted_type->data.structure.is_slice);
-                assert(actual_type->id == TypeTableEntryIdStruct);
-                assert(actual_type->data.structure.is_slice);
-
-                TypeTableEntry *actual_pointer_type = actual_type->data.structure.fields[0].type_entry;
-                TypeTableEntry *actual_child_type = actual_pointer_type->data.pointer.child_type;
-                TypeTableEntry *wanted_pointer_type = wanted_type->data.structure.fields[0].type_entry;
-                TypeTableEntry *wanted_child_type = wanted_pointer_type->data.pointer.child_type;
-
-                set_debug_source_node(g, node);
-
-                size_t actual_ptr_index = actual_type->data.structure.fields[0].gen_index;
-                size_t actual_len_index = actual_type->data.structure.fields[1].gen_index;
-                size_t wanted_ptr_index = wanted_type->data.structure.fields[0].gen_index;
-                size_t wanted_len_index = wanted_type->data.structure.fields[1].gen_index;
-
-                LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, expr_val, actual_ptr_index, "");
-                LLVMValueRef src_ptr = LLVMBuildLoad(g->builder, src_ptr_ptr, "");
-                LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, src_ptr,
-                        wanted_type->data.structure.fields[0].type_entry->type_ref, "");
-                LLVMValueRef dest_ptr_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr,
-                        wanted_ptr_index, "");
-                LLVMBuildStore(g->builder, src_ptr_casted, dest_ptr_ptr);
-
-                LLVMValueRef src_len_ptr = LLVMBuildStructGEP(g->builder, expr_val, actual_len_index, "");
-                LLVMValueRef src_len = LLVMBuildLoad(g->builder, src_len_ptr, "");
-                uint64_t src_size = type_size(g, actual_child_type);
-                uint64_t dest_size = type_size(g, wanted_child_type);
-
-                LLVMValueRef new_len;
-                if (dest_size == 1) {
-                    LLVMValueRef src_size_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, src_size, false);
-                    new_len = LLVMBuildMul(g->builder, src_len, src_size_val, "");
-                } else if (src_size == 1) {
-                    LLVMValueRef dest_size_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, dest_size, false);
-                    if (want_debug_safety(g, node)) {
-                        LLVMValueRef remainder_val = LLVMBuildURem(g->builder, src_len, dest_size_val, "");
-                        LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_usize->type_ref);
-                        LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, "");
-                        LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "SliceWidenOk");
-                        LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "SliceWidenFail");
-                        LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
-
-                        LLVMPositionBuilderAtEnd(g->builder, fail_block);
-                        gen_debug_safety_crash(g);
-
-                        LLVMPositionBuilderAtEnd(g->builder, ok_block);
-                    }
-                    new_len = ZigLLVMBuildExactUDiv(g->builder, src_len, dest_size_val, "");
-                } else {
-                    zig_unreachable();
-                }
-
-                LLVMValueRef dest_len_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr,
-                        wanted_len_index, "");
-                LLVMBuildStore(g->builder, new_len, dest_len_ptr);
-
-
-                return cast_expr->tmp_ptr;
-            }
-        case CastOpBytesToSlice:
-            {
-                assert(cast_expr->tmp_ptr);
-                assert(wanted_type->id == TypeTableEntryIdStruct);
-                assert(wanted_type->data.structure.is_slice);
-                assert(actual_type->id == TypeTableEntryIdArray);
-
-                TypeTableEntry *wanted_pointer_type = wanted_type->data.structure.fields[0].type_entry;
-                TypeTableEntry *wanted_child_type = wanted_pointer_type->data.pointer.child_type;
-
-                set_debug_source_node(g, node);
-
-                size_t wanted_ptr_index = wanted_type->data.structure.fields[0].gen_index;
-                LLVMValueRef dest_ptr_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, wanted_ptr_index, "");
-                LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, expr_val, wanted_pointer_type->type_ref, "");
-                LLVMBuildStore(g->builder, src_ptr_casted, dest_ptr_ptr);
-
-                size_t wanted_len_index = wanted_type->data.structure.fields[1].gen_index;
-                LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, wanted_len_index, "");
-                LLVMValueRef len_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref,
-                        actual_type->data.array.len / type_size(g, wanted_child_type), false);
-                LLVMBuildStore(g->builder, len_val, len_ptr);
-
-                return cast_expr->tmp_ptr;
-            }
-        case CastOpIntToFloat:
-            assert(actual_type->id == TypeTableEntryIdInt);
-            if (actual_type->data.integral.is_signed) {
-                set_debug_source_node(g, node);
-                return LLVMBuildSIToFP(g->builder, expr_val, wanted_type->type_ref, "");
-            } else {
-                set_debug_source_node(g, node);
-                return LLVMBuildUIToFP(g->builder, expr_val, wanted_type->type_ref, "");
-            }
-        case CastOpFloatToInt:
-            assert(wanted_type->id == TypeTableEntryIdInt);
-            if (wanted_type->data.integral.is_signed) {
-                set_debug_source_node(g, node);
-                return LLVMBuildFPToSI(g->builder, expr_val, wanted_type->type_ref, "");
-            } else {
-                set_debug_source_node(g, node);
-                return LLVMBuildFPToUI(g->builder, expr_val, wanted_type->type_ref, "");
-            }
-
-        case CastOpBoolToInt:
-            assert(wanted_type->id == TypeTableEntryIdInt);
-            assert(actual_type->id == TypeTableEntryIdBool);
-            set_debug_source_node(g, node);
-            return LLVMBuildZExt(g->builder, expr_val, wanted_type->type_ref, "");
-
-        case CastOpIntToEnum:
-            return gen_widen_or_shorten(g, node, actual_type, wanted_type->data.enumeration.tag_type, expr_val);
-        case CastOpEnumToInt:
-            return gen_widen_or_shorten(g, node, actual_type->data.enumeration.tag_type, wanted_type, expr_val);
-    }
-    zig_unreachable();
-}
-
-
 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);
-    } else if (node->data.fn_call_expr.cast_op != CastOpNoCast) {
-        return gen_cast_expr(g, node);
     }
 
     FnTableEntry *fn_table_entry = node->data.fn_call_expr.fn_entry;
@@ -1186,7 +920,6 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {
         }
     }
 
-    set_debug_source_node(g, node);
     LLVMValueRef result = ZigLLVMBuildCall(g->builder, fn_val,
             gen_param_values, gen_param_index, fn_type->data.fn.calling_convention, "");
 
@@ -1209,7 +942,6 @@ static LLVMValueRef gen_array_base_ptr(CodeGen *g, AstNode *node) {
         array_ptr = gen_field_access_expr(g, node, true);
         if (type_entry->id == TypeTableEntryIdPointer) {
             // we have a double pointer so we must dereference it once
-            set_debug_source_node(g, node);
             array_ptr = LLVMBuildLoad(g->builder, array_ptr, "");
         }
     } else {
@@ -1234,20 +966,18 @@ static LLVMValueRef gen_array_elem_ptr(CodeGen *g, AstNode *source_node, LLVMVal
         if (want_debug_safety(g, source_node)) {
             LLVMValueRef end = LLVMConstInt(g->builtin_types.entry_usize->type_ref,
                     array_type->data.array.len, false);
-            add_bounds_check(g, source_node, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, end);
+            add_bounds_check(g, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, end);
         }
         LLVMValueRef indices[] = {
             LLVMConstNull(g->builtin_types.entry_usize->type_ref),
             subscript_value
         };
-        set_debug_source_node(g, source_node);
         return LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, "");
     } else if (array_type->id == TypeTableEntryIdPointer) {
         assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind);
         LLVMValueRef indices[] = {
             subscript_value
         };
-        set_debug_source_node(g, source_node);
         return LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 1, "");
     } else if (array_type->id == TypeTableEntryIdStruct) {
         assert(array_type->data.structure.is_slice);
@@ -1255,15 +985,13 @@ static LLVMValueRef gen_array_elem_ptr(CodeGen *g, AstNode *source_node, LLVMVal
         assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind);
 
         if (want_debug_safety(g, source_node)) {
-            set_debug_source_node(g, source_node);
             size_t len_index = array_type->data.structure.fields[1].gen_index;
             assert(len_index != SIZE_MAX);
             LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, len_index, "");
             LLVMValueRef len = LLVMBuildLoad(g->builder, len_ptr, "");
-            add_bounds_check(g, source_node, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, len);
+            add_bounds_check(g, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, len);
         }
 
-        set_debug_source_node(g, source_node);
         size_t ptr_index = array_type->data.structure.fields[0].gen_index;
         assert(ptr_index != SIZE_MAX);
         LLVMValueRef ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, ptr_index, "");
@@ -1302,7 +1030,6 @@ static LLVMValueRef gen_field_ptr(CodeGen *g, AstNode *node, TypeTableEntry **ou
         assert(var);
 
         if (var->type->id == TypeTableEntryIdPointer) {
-            set_debug_source_node(g, node);
             struct_ptr = LLVMBuildLoad(g->builder, var->value_ref, "");
         } else {
             struct_ptr = var->value_ref;
@@ -1312,7 +1039,6 @@ static LLVMValueRef gen_field_ptr(CodeGen *g, AstNode *node, TypeTableEntry **ou
         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
-            set_debug_source_node(g, node);
             struct_ptr = LLVMBuildLoad(g->builder, struct_ptr, "");
         }
     } else {
@@ -1325,7 +1051,6 @@ static LLVMValueRef gen_field_ptr(CodeGen *g, AstNode *node, TypeTableEntry **ou
     size_t gen_field_index = node->data.field_access_expr.type_struct_field->gen_index;
     assert(gen_field_index != SIZE_MAX);
 
-    set_debug_source_node(g, node);
     return LLVMBuildStructGEP(g->builder, struct_ptr, gen_field_index, "");
 }
 
@@ -1348,15 +1073,14 @@ static LLVMValueRef gen_slice_expr(CodeGen *g, AstNode *node) {
         }
 
         if (want_debug_safety(g, node)) {
-            add_bounds_check(g, node, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val);
+            add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val);
             if (node->data.slice_expr.end) {
                 LLVMValueRef array_end = LLVMConstInt(g->builtin_types.entry_usize->type_ref,
                         array_type->data.array.len, false);
-                add_bounds_check(g, node, end_val, LLVMIntEQ, nullptr, LLVMIntULE, array_end);
+                add_bounds_check(g, end_val, LLVMIntEQ, nullptr, LLVMIntULE, array_end);
             }
         }
 
-        set_debug_source_node(g, node);
         LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 0, "");
         LLVMValueRef indices[] = {
             LLVMConstNull(g->builtin_types.entry_usize->type_ref),
@@ -1375,10 +1099,9 @@ static LLVMValueRef gen_slice_expr(CodeGen *g, AstNode *node) {
         LLVMValueRef end_val = gen_expr(g, node->data.slice_expr.end);
 
         if (want_debug_safety(g, node)) {
-            add_bounds_check(g, node, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val);
+            add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val);
         }
 
-        set_debug_source_node(g, node);
         LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 0, "");
         LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, "");
         LLVMBuildStore(g->builder, slice_start_ptr, ptr_field_ptr);
@@ -1400,7 +1123,6 @@ static LLVMValueRef gen_slice_expr(CodeGen *g, AstNode *node) {
 
         LLVMValueRef prev_end = nullptr;
         if (!node->data.slice_expr.end || want_debug_safety(g, node)) {
-            set_debug_source_node(g, node);
             LLVMValueRef src_len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, len_index, "");
             prev_end = LLVMBuildLoad(g->builder, src_len_ptr, "");
         }
@@ -1415,13 +1137,12 @@ static LLVMValueRef gen_slice_expr(CodeGen *g, AstNode *node) {
 
         if (want_debug_safety(g, node)) {
             assert(prev_end);
-            add_bounds_check(g, node, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val);
+            add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val);
             if (node->data.slice_expr.end) {
-                add_bounds_check(g, node, end_val, LLVMIntEQ, nullptr, LLVMIntULE, prev_end);
+                add_bounds_check(g, end_val, LLVMIntEQ, nullptr, LLVMIntULE, prev_end);
             }
         }
 
-        set_debug_source_node(g, node);
         LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, ptr_index, "");
         LLVMValueRef src_ptr = LLVMBuildLoad(g->builder, src_ptr_ptr, "");
         LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, ptr_index, "");
@@ -1461,7 +1182,6 @@ static LLVMValueRef gen_array_access_expr(CodeGen *g, AstNode *node, bool is_lva
     if (is_lvalue || !ptr || handle_is_ptr(child_type)) {
         return ptr;
     } else {
-        set_debug_source_node(g, node);
         return LLVMBuildLoad(g->builder, ptr, "");
     }
 }
@@ -1471,7 +1191,7 @@ static LLVMValueRef gen_variable(CodeGen *g, AstNode *source_node, VariableTable
         return nullptr;
     } else {
         assert(variable->value_ref);
-        return get_handle_value(g, source_node, variable->value_ref, variable->type);
+        return get_handle_value(g, variable->value_ref, variable->type);
     }
 }
 
@@ -1494,7 +1214,6 @@ static LLVMValueRef gen_field_access_expr(CodeGen *g, AstNode *node, bool is_lva
         if (is_lvalue || handle_is_ptr(type_entry)) {
             return ptr;
         } else {
-            set_debug_source_node(g, node);
             return LLVMBuildLoad(g->builder, ptr, "");
         }
     } else if (struct_type->id == TypeTableEntryIdMetaType) {
@@ -1631,7 +1350,6 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {
         case PrefixOpNegationWrap:
             {
                 LLVMValueRef expr = gen_expr(g, expr_node);
-                set_debug_source_node(g, node);
                 if (expr_type->id == TypeTableEntryIdFloat) {
                     return LLVMBuildFNeg(g->builder, expr, "");
                 } else if (expr_type->id == TypeTableEntryIdInt) {
@@ -1653,13 +1371,11 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {
             {
                 LLVMValueRef expr = gen_expr(g, expr_node);
                 LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(expr));
-                set_debug_source_node(g, node);
                 return LLVMBuildICmp(g->builder, LLVMIntEQ, expr, zero, "");
             }
         case PrefixOpBinNot:
             {
                 LLVMValueRef expr = gen_expr(g, expr_node);
-                set_debug_source_node(g, node);
                 return LLVMBuildNot(g->builder, expr, "");
             }
         case PrefixOpAddressOf:
@@ -1677,7 +1393,7 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {
                     return nullptr;
                 } else {
                     TypeTableEntry *child_type = expr_type->data.pointer.child_type;
-                    return get_handle_value(g, node, expr, child_type);
+                    return get_handle_value(g, expr, child_type);
                 }
             }
         case PrefixOpMaybe:
@@ -1698,13 +1414,11 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {
                 if (want_debug_safety(g, node)) {
                     LLVMValueRef err_val;
                     if (type_has_bits(child_type)) {
-                        set_debug_source_node(g, node);
                         LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, expr_val, 0, "");
                         err_val = LLVMBuildLoad(g->builder, err_val_ptr, "");
                     } else {
                         err_val = expr_val;
                     }
-                    set_debug_source_node(g, node);
                     LLVMValueRef zero = LLVMConstNull(g->err_tag_type->type_ref);
                     LLVMValueRef cond_val = LLVMBuildICmp(g->builder, LLVMIntEQ, err_val, zero, "");
                     LLVMBasicBlockRef err_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapErrError");
@@ -1719,7 +1433,7 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {
 
                 if (type_has_bits(child_type)) {
                     LLVMValueRef child_val_ptr = LLVMBuildStructGEP(g->builder, expr_val, 1, "");
-                    return get_handle_value(g, expr_node, child_val_ptr, child_type);
+                    return get_handle_value(g, child_val_ptr, child_type);
                 } else {
                     return nullptr;
                 }
@@ -1733,7 +1447,6 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {
                 TypeTableEntry *child_type = expr_type->data.maybe.child_type;
 
                 if (want_debug_safety(g, node)) {
-                    set_debug_source_node(g, node);
                     LLVMValueRef cond_val;
                     if (child_type->id == TypeTableEntryIdPointer ||
                         child_type->id == TypeTableEntryIdFn)
@@ -1761,9 +1474,8 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {
                 {
                     return expr_val;
                 } else {
-                    set_debug_source_node(g, node);
                     LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, expr_val, 0, "");
-                    return get_handle_value(g, node, maybe_field_ptr, child_type);
+                    return get_handle_value(g, maybe_field_ptr, child_type);
                 }
             }
     }
@@ -1773,7 +1485,6 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {
 static LLVMValueRef gen_div(CodeGen *g, AstNode *source_node, LLVMValueRef val1, LLVMValueRef val2,
         TypeTableEntry *type_entry, bool exact)
 {
-    set_debug_source_node(g, source_node);
 
     if (want_debug_safety(g, source_node)) {
         LLVMValueRef zero = LLVMConstNull(type_entry->type_ref);
@@ -1846,22 +1557,18 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node,
     switch (bin_op) {
         case BinOpTypeBinOr:
         case BinOpTypeAssignBitOr:
-            set_debug_source_node(g, source_node);
             return LLVMBuildOr(g->builder, val1, val2, "");
         case BinOpTypeBinXor:
         case BinOpTypeAssignBitXor:
-            set_debug_source_node(g, source_node);
             return LLVMBuildXor(g->builder, val1, val2, "");
         case BinOpTypeBinAnd:
         case BinOpTypeAssignBitAnd:
-            set_debug_source_node(g, source_node);
             return LLVMBuildAnd(g->builder, val1, val2, "");
         case BinOpTypeBitShiftLeft:
         case BinOpTypeBitShiftLeftWrap:
         case BinOpTypeAssignBitShiftLeft:
         case BinOpTypeAssignBitShiftLeftWrap:
             {
-                set_debug_source_node(g, source_node);
                 assert(op1_type->id == TypeTableEntryIdInt);
                 bool is_wrapping = (bin_op == BinOpTypeBitShiftLeftWrap) ||
                     (bin_op == BinOpTypeAssignBitShiftLeftWrap);
@@ -1880,7 +1587,6 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node,
             assert(op1_type->id == TypeTableEntryIdInt);
             assert(op2_type->id == TypeTableEntryIdInt);
 
-            set_debug_source_node(g, source_node);
             if (op1_type->data.integral.is_signed) {
                 return LLVMBuildAShr(g->builder, val1, val2, "");
             } else {
@@ -1890,7 +1596,6 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node,
         case BinOpTypeAddWrap:
         case BinOpTypeAssignPlus:
         case BinOpTypeAssignPlusWrap:
-            set_debug_source_node(g, source_node);
             if (op1_type->id == TypeTableEntryIdFloat) {
                 return LLVMBuildFAdd(g->builder, val1, val2, "");
             } else if (op1_type->id == TypeTableEntryIdInt) {
@@ -1911,7 +1616,6 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node,
         case BinOpTypeSubWrap:
         case BinOpTypeAssignMinus:
         case BinOpTypeAssignMinusWrap:
-            set_debug_source_node(g, source_node);
             if (op1_type->id == TypeTableEntryIdFloat) {
                 return LLVMBuildFSub(g->builder, val1, val2, "");
             } else if (op1_type->id == TypeTableEntryIdInt) {
@@ -1932,7 +1636,6 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node,
         case BinOpTypeMultWrap:
         case BinOpTypeAssignTimes:
         case BinOpTypeAssignTimesWrap:
-            set_debug_source_node(g, source_node);
             if (op1_type->id == TypeTableEntryIdFloat) {
                 return LLVMBuildFMul(g->builder, val1, val2, "");
             } else if (op1_type->id == TypeTableEntryIdInt) {
@@ -1954,7 +1657,6 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node,
             return gen_div(g, source_node, val1, val2, op1_type, false);
         case BinOpTypeMod:
         case BinOpTypeAssignMod:
-            set_debug_source_node(g, source_node);
             if (op1_type->id == TypeTableEntryIdFloat) {
                 return LLVMBuildFRem(g->builder, val1, val2, "");
             } else {
@@ -2044,7 +1746,6 @@ static LLVMValueRef gen_cmp_expr(CodeGen *g, AstNode *node) {
     TypeTableEntry *op2_type = get_expr_type(node->data.bin_op_expr.op2);
     assert(op1_type == op2_type);
 
-    set_debug_source_node(g, node);
     if (op1_type->id == TypeTableEntryIdFloat) {
         LLVMRealPredicate pred = cmp_op_to_real_predicate(node->data.bin_op_expr.bin_op);
         return LLVMBuildFCmp(g->builder, pred, val1, val2, "");
@@ -2081,18 +1782,15 @@ static LLVMValueRef gen_bool_and_expr(CodeGen *g, AstNode *node) {
     // block for when val1 == false (don't even evaluate the second part)
     LLVMBasicBlockRef false_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoolAndFalse");
 
-    set_debug_source_node(g, node);
     LLVMBuildCondBr(g->builder, val1, true_block, false_block);
 
     LLVMPositionBuilderAtEnd(g->builder, true_block);
     LLVMValueRef val2 = gen_expr(g, node->data.bin_op_expr.op2);
     LLVMBasicBlockRef post_val2_block = LLVMGetInsertBlock(g->builder);
 
-    set_debug_source_node(g, node);
     LLVMBuildBr(g->builder, false_block);
 
     LLVMPositionBuilderAtEnd(g->builder, false_block);
-    set_debug_source_node(g, node);
     LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMInt1Type(), "");
     LLVMValueRef incoming_values[2] = {val1, val2};
     LLVMBasicBlockRef incoming_blocks[2] = {post_val1_block, post_val2_block};
@@ -2112,7 +1810,6 @@ static LLVMValueRef gen_bool_or_expr(CodeGen *g, AstNode *expr_node) {
     // block for when val1 == true (don't even evaluate the second part)
     LLVMBasicBlockRef true_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoolOrTrue");
 
-    set_debug_source_node(g, expr_node);
     LLVMBuildCondBr(g->builder, val1, true_block, false_block);
 
     LLVMPositionBuilderAtEnd(g->builder, false_block);
@@ -2120,11 +1817,9 @@ static LLVMValueRef gen_bool_or_expr(CodeGen *g, AstNode *expr_node) {
 
     LLVMBasicBlockRef post_val2_block = LLVMGetInsertBlock(g->builder);
 
-    set_debug_source_node(g, expr_node);
     LLVMBuildBr(g->builder, true_block);
 
     LLVMPositionBuilderAtEnd(g->builder, true_block);
-    set_debug_source_node(g, expr_node);
     LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMInt1Type(), "");
     LLVMValueRef incoming_values[2] = {val1, val2};
     LLVMBasicBlockRef incoming_blocks[2] = {post_val1_block, post_val2_block};
@@ -2133,14 +1828,13 @@ static LLVMValueRef gen_bool_or_expr(CodeGen *g, AstNode *expr_node) {
     return phi;
 }
 
-static LLVMValueRef gen_struct_memcpy(CodeGen *g, AstNode *source_node, LLVMValueRef src, LLVMValueRef dest,
+static LLVMValueRef gen_struct_memcpy(CodeGen *g, LLVMValueRef src, LLVMValueRef dest,
         TypeTableEntry *type_entry)
 {
     assert(handle_is_ptr(type_entry));
 
     LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0);
 
-    set_debug_source_node(g, source_node);
     LLVMValueRef src_ptr = LLVMBuildBitCast(g->builder, src, ptr_u8, "");
     LLVMValueRef dest_ptr = LLVMBuildBitCast(g->builder, dest, ptr_u8, "");
 
@@ -2172,18 +1866,16 @@ static LLVMValueRef gen_assign_raw(CodeGen *g, AstNode *source_node, BinOpType b
         assert(op1_type == op2_type);
         assert(bin_op == BinOpTypeAssign);
 
-        return gen_struct_memcpy(g, source_node, value, target_ref, op1_type);
+        return gen_struct_memcpy(g, value, target_ref, op1_type);
     }
 
     if (bin_op != BinOpTypeAssign) {
         assert(source_node->type == NodeTypeBinOpExpr);
-        set_debug_source_node(g, source_node->data.bin_op_expr.op1);
         LLVMValueRef left_value = LLVMBuildLoad(g->builder, target_ref, "");
 
         value = gen_arithmetic_bin_op(g, source_node, left_value, value, op1_type, op2_type, bin_op);
     }
 
-    set_debug_source_node(g, source_node);
     LLVMBuildStore(g->builder, value, target_ref);
     return nullptr;
 }
@@ -2214,9 +1906,8 @@ static LLVMValueRef gen_unwrap_maybe(CodeGen *g, AstNode *node, LLVMValueRef may
     {
         return maybe_struct_ref;
     } else {
-        set_debug_source_node(g, node);
         LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_struct_ref, 0, "");
-        return get_handle_value(g, node, maybe_field_ptr, child_type);
+        return get_handle_value(g, maybe_field_ptr, child_type);
     }
 }
 
@@ -2240,7 +1931,6 @@ static LLVMValueRef gen_unwrap_maybe_expr(CodeGen *g, AstNode *node) {
         cond_value = LLVMBuildICmp(g->builder, LLVMIntNE, maybe_struct_ref,
                 LLVMConstNull(child_type->type_ref), "");
     } else {
-        set_debug_source_node(g, node);
         LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_struct_ref, 1, "");
         cond_value = LLVMBuildLoad(g->builder, maybe_field_ptr, "");
     }
@@ -2255,21 +1945,18 @@ static LLVMValueRef gen_unwrap_maybe_expr(CodeGen *g, AstNode *node) {
 
     LLVMPositionBuilderAtEnd(g->builder, non_null_block);
     LLVMValueRef non_null_result = gen_unwrap_maybe(g, op1_node, maybe_struct_ref);
-    set_debug_source_node(g, node);
     LLVMBuildBr(g->builder, end_block);
     LLVMBasicBlockRef post_non_null_result_block = LLVMGetInsertBlock(g->builder);
 
     LLVMPositionBuilderAtEnd(g->builder, null_block);
     LLVMValueRef null_result = gen_expr(g, op2_node);
     if (null_reachable) {
-        set_debug_source_node(g, node);
         LLVMBuildBr(g->builder, end_block);
     }
     LLVMBasicBlockRef post_null_result_block = LLVMGetInsertBlock(g->builder);
 
     LLVMPositionBuilderAtEnd(g->builder, end_block);
     if (null_reachable) {
-        set_debug_source_node(g, node);
         LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(non_null_result), "");
         LLVMValueRef incoming_values[2] = {non_null_result, null_result};
         LLVMBasicBlockRef incoming_blocks[2] = {post_non_null_result_block, post_null_result_block};
@@ -2351,7 +2038,6 @@ static LLVMValueRef gen_unwrap_err_expr(CodeGen *g, AstNode *node) {
     assert(expr_type->id == TypeTableEntryIdErrorUnion);
     TypeTableEntry *child_type = expr_type->data.error.child_type;
     LLVMValueRef err_val;
-    set_debug_source_node(g, node);
     if (handle_is_ptr(expr_type)) {
         LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, expr_val, 0, "");
         err_val = LLVMBuildLoad(g->builder, err_val_ptr, "");
@@ -2377,7 +2063,6 @@ static LLVMValueRef gen_unwrap_err_expr(CodeGen *g, AstNode *node) {
         LLVMBuildStore(g->builder, err_val, var->value_ref);
     }
     LLVMValueRef err_result = gen_expr(g, op2);
-    set_debug_source_node(g, node);
     if (have_end_block) {
         LLVMBuildBr(g->builder, end_block);
     } else if (err_reachable) {
@@ -2389,7 +2074,7 @@ static LLVMValueRef gen_unwrap_err_expr(CodeGen *g, AstNode *node) {
         return nullptr;
     }
     LLVMValueRef child_val_ptr = LLVMBuildStructGEP(g->builder, expr_val, 1, "");
-    LLVMValueRef child_val = get_handle_value(g, node, child_val_ptr, child_type);
+    LLVMValueRef child_val = get_handle_value(g, child_val_ptr, child_type);
 
     if (!have_end_block) {
         return child_val;
@@ -2452,17 +2137,14 @@ static LLVMValueRef gen_return(CodeGen *g, AstNode *source_node, LLVMValueRef va
     bool is_extern = g->cur_fn->type_entry->data.fn.fn_type_id.is_extern;
     if (handle_is_ptr(return_type)) {
         if (is_extern) {
-            set_debug_source_node(g, source_node);
             LLVMValueRef by_val_value = LLVMBuildLoad(g->builder, value, "");
             LLVMBuildRet(g->builder, by_val_value);
         } else {
             assert(g->cur_ret_ptr);
             gen_assign_raw(g, source_node, BinOpTypeAssign, g->cur_ret_ptr, value, return_type, return_type);
-            set_debug_source_node(g, source_node);
             LLVMBuildRetVoid(g->builder);
         }
     } else {
-        set_debug_source_node(g, source_node);
         LLVMBuildRet(g->builder, value);
     }
     return nullptr;
@@ -2504,7 +2186,6 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) {
                 LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ErrRetReturn");
                 LLVMBasicBlockRef continue_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ErrRetContinue");
 
-                set_debug_source_node(g, node);
                 LLVMValueRef err_val;
                 if (type_has_bits(child_type)) {
                     LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, value, 0, "");
@@ -2524,7 +2205,6 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) {
                     if (type_has_bits(return_type->data.error.child_type)) {
                         assert(g->cur_ret_ptr);
 
-                        set_debug_source_node(g, node);
                         LLVMValueRef tag_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, 0, "");
                         LLVMBuildStore(g->builder, err_val, tag_ptr);
                         LLVMBuildRetVoid(g->builder);
@@ -2537,9 +2217,8 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) {
 
                 LLVMPositionBuilderAtEnd(g->builder, continue_block);
                 if (type_has_bits(child_type)) {
-                    set_debug_source_node(g, node);
                     LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, value, 1, "");
-                    return get_handle_value(g, node, val_ptr, child_type);
+                    return get_handle_value(g, val_ptr, child_type);
                 } else {
                     return nullptr;
                 }
@@ -2552,7 +2231,6 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) {
                 LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeRetReturn");
                 LLVMBasicBlockRef continue_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeRetContinue");
 
-                set_debug_source_node(g, node);
                 LLVMValueRef maybe_val_ptr = LLVMBuildStructGEP(g->builder, value, 1, "");
                 LLVMValueRef is_non_null = LLVMBuildLoad(g->builder, maybe_val_ptr, "");
 
@@ -2566,7 +2244,6 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) {
                 if (handle_is_ptr(return_type)) {
                     assert(g->cur_ret_ptr);
 
-                    set_debug_source_node(g, node);
                     LLVMValueRef maybe_bit_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, 1, "");
                     LLVMBuildStore(g->builder, zero, maybe_bit_ptr);
                     LLVMBuildRetVoid(g->builder);
@@ -2577,9 +2254,8 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) {
 
                 LLVMPositionBuilderAtEnd(g->builder, continue_block);
                 if (type_has_bits(child_type)) {
-                    set_debug_source_node(g, node);
                     LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, value, 0, "");
-                    return get_handle_value(g, node, val_ptr, child_type);
+                    return get_handle_value(g, val_ptr, child_type);
                 } else {
                     return nullptr;
                 }
@@ -2610,7 +2286,6 @@ static LLVMValueRef gen_if_bool_expr_raw(CodeGen *g, AstNode *source_node, LLVMV
         endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "EndIf");
     }
 
-    set_debug_source_node(g, source_node);
     LLVMBuildCondBr(g->builder, cond_value, then_block, else_block);
 
     LLVMPositionBuilderAtEnd(g->builder, then_block);
@@ -2632,7 +2307,6 @@ static LLVMValueRef gen_if_bool_expr_raw(CodeGen *g, AstNode *source_node, LLVMV
     if (then_endif_reachable || else_endif_reachable) {
         LLVMPositionBuilderAtEnd(g->builder, endif_block);
         if (use_then_value && use_else_value) {
-            set_debug_source_node(g, source_node);
             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};
@@ -2697,7 +2371,7 @@ static LLVMValueRef gen_if_var_then_block(CodeGen *g, AstNode *node, VariableTab
             payload_val = init_val;
         } else {
             LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, init_val, 0, "");
-            payload_val = get_handle_value(g, node, payload_ptr, child_type);
+            payload_val = get_handle_value(g, payload_ptr, child_type);
         }
         gen_assign_raw(g, node, BinOpTypeAssign, variable->value_ref, payload_val,
                 variable->type, child_type);
@@ -2736,10 +2410,8 @@ static LLVMValueRef gen_if_var_expr(CodeGen *g, AstNode *node) {
 
     LLVMValueRef cond_value;
     if (maybe_is_ptr) {
-        set_debug_source_node(g, node);
         cond_value = LLVMBuildICmp(g->builder, LLVMIntNE, init_val, LLVMConstNull(child_type->type_ref), "");
     } else {
-        set_debug_source_node(g, node);
         LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, init_val, 1, "");
         cond_value = LLVMBuildLoad(g->builder, maybe_field_ptr, "");
     }
@@ -2760,7 +2432,6 @@ static LLVMValueRef gen_if_var_expr(CodeGen *g, AstNode *node) {
         endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeEndIf");
     }
 
-    set_debug_source_node(g, node);
     LLVMBuildCondBr(g->builder, cond_value, then_block, else_block);
 
     LLVMPositionBuilderAtEnd(g->builder, then_block);
@@ -2810,7 +2481,7 @@ static LLVMValueRef ir_render_load_var(CodeGen *g, IrExecutable *executable,
         return nullptr;
 
     assert(var->value_ref);
-    return get_handle_value(g, load_var_instruction->base.source_node, var->value_ref, var->type);
+    return get_handle_value(g, var->value_ref, var->type);
 }
 
 static LLVMValueRef ir_render_bin_op_bool(CodeGen *g, IrExecutable *executable,
@@ -2894,6 +2565,233 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
     zig_unreachable();
 }
 
+static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable,
+        IrInstructionCast *cast_instruction)
+{
+    TypeTableEntry *actual_type = cast_instruction->value->type_entry;
+    TypeTableEntry *wanted_type = cast_instruction->base.type_entry;
+    LLVMValueRef expr_val = cast_instruction->value->llvm_value;
+    assert(expr_val);
+
+    switch (cast_instruction->cast_op) {
+        case CastOpNoCast:
+            zig_unreachable();
+        case CastOpNoop:
+            return expr_val;
+        case CastOpErrToInt:
+            assert(actual_type->id == TypeTableEntryIdErrorUnion);
+            if (!type_has_bits(actual_type->data.error.child_type)) {
+                return gen_widen_or_shorten(g, cast_instruction->base.source_node,
+                        g->err_tag_type, wanted_type, expr_val);
+            } else {
+                zig_panic("TODO");
+            }
+        case CastOpMaybeWrap:
+            {
+                assert(cast_instruction->tmp_ptr);
+                assert(wanted_type->id == TypeTableEntryIdMaybe);
+                assert(actual_type);
+
+                TypeTableEntry *child_type = wanted_type->data.maybe.child_type;
+
+                if (child_type->id == TypeTableEntryIdPointer ||
+                    child_type->id == TypeTableEntryIdFn)
+                {
+                    return expr_val;
+                } else {
+                    LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, 0, "");
+                    gen_assign_raw(g, cast_instruction->base.source_node, BinOpTypeAssign,
+                            val_ptr, expr_val, child_type, actual_type);
+
+                    LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, 1, "");
+                    LLVMBuildStore(g->builder, LLVMConstAllOnes(LLVMInt1Type()), maybe_ptr);
+                }
+
+                return cast_instruction->tmp_ptr;
+            }
+        case CastOpNullToMaybe:
+            // handled by constant expression evaluator
+            zig_unreachable();
+        case CastOpErrorWrap:
+            {
+                assert(wanted_type->id == TypeTableEntryIdErrorUnion);
+                TypeTableEntry *child_type = wanted_type->data.error.child_type;
+                LLVMValueRef ok_err_val = LLVMConstNull(g->err_tag_type->type_ref);
+
+                if (!type_has_bits(child_type)) {
+                    return ok_err_val;
+                } else {
+                    assert(cast_instruction->tmp_ptr);
+                    assert(wanted_type->id == TypeTableEntryIdErrorUnion);
+                    assert(actual_type);
+
+                    LLVMValueRef err_tag_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, 0, "");
+                    LLVMBuildStore(g->builder, ok_err_val, err_tag_ptr);
+
+                    LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, 1, "");
+                    gen_assign_raw(g, cast_instruction->base.source_node, BinOpTypeAssign,
+                            payload_ptr, expr_val, child_type, actual_type);
+
+                    return cast_instruction->tmp_ptr;
+                }
+            }
+        case CastOpPureErrorWrap:
+            assert(wanted_type->id == TypeTableEntryIdErrorUnion);
+
+            if (!type_has_bits(wanted_type->data.error.child_type)) {
+                return expr_val;
+            } else {
+                zig_panic("TODO");
+            }
+        case CastOpPtrToInt:
+            return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, "");
+        case CastOpIntToPtr:
+            return LLVMBuildIntToPtr(g->builder, expr_val, wanted_type->type_ref, "");
+        case CastOpPointerReinterpret:
+            return LLVMBuildBitCast(g->builder, expr_val, wanted_type->type_ref, "");
+        case CastOpWidenOrShorten:
+            return gen_widen_or_shorten(g, cast_instruction->base.source_node, actual_type, wanted_type, expr_val);
+        case CastOpToUnknownSizeArray:
+            {
+                assert(cast_instruction->tmp_ptr);
+                assert(wanted_type->id == TypeTableEntryIdStruct);
+                assert(wanted_type->data.structure.is_slice);
+
+                TypeTableEntry *pointer_type = wanted_type->data.structure.fields[0].type_entry;
+
+
+                size_t ptr_index = wanted_type->data.structure.fields[0].gen_index;
+                if (ptr_index != SIZE_MAX) {
+                    LLVMValueRef ptr_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, ptr_index, "");
+                    LLVMValueRef expr_bitcast = LLVMBuildBitCast(g->builder, expr_val, pointer_type->type_ref, "");
+                    LLVMBuildStore(g->builder, expr_bitcast, ptr_ptr);
+                }
+
+                size_t len_index = wanted_type->data.structure.fields[1].gen_index;
+                LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, len_index, "");
+                LLVMValueRef len_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref,
+                        actual_type->data.array.len, false);
+                LLVMBuildStore(g->builder, len_val, len_ptr);
+
+                return cast_instruction->tmp_ptr;
+            }
+        case CastOpResizeSlice:
+            {
+                assert(cast_instruction->tmp_ptr);
+                assert(wanted_type->id == TypeTableEntryIdStruct);
+                assert(wanted_type->data.structure.is_slice);
+                assert(actual_type->id == TypeTableEntryIdStruct);
+                assert(actual_type->data.structure.is_slice);
+
+                TypeTableEntry *actual_pointer_type = actual_type->data.structure.fields[0].type_entry;
+                TypeTableEntry *actual_child_type = actual_pointer_type->data.pointer.child_type;
+                TypeTableEntry *wanted_pointer_type = wanted_type->data.structure.fields[0].type_entry;
+                TypeTableEntry *wanted_child_type = wanted_pointer_type->data.pointer.child_type;
+
+
+                size_t actual_ptr_index = actual_type->data.structure.fields[0].gen_index;
+                size_t actual_len_index = actual_type->data.structure.fields[1].gen_index;
+                size_t wanted_ptr_index = wanted_type->data.structure.fields[0].gen_index;
+                size_t wanted_len_index = wanted_type->data.structure.fields[1].gen_index;
+
+                LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, expr_val, actual_ptr_index, "");
+                LLVMValueRef src_ptr = LLVMBuildLoad(g->builder, src_ptr_ptr, "");
+                LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, src_ptr,
+                        wanted_type->data.structure.fields[0].type_entry->type_ref, "");
+                LLVMValueRef dest_ptr_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr,
+                        wanted_ptr_index, "");
+                LLVMBuildStore(g->builder, src_ptr_casted, dest_ptr_ptr);
+
+                LLVMValueRef src_len_ptr = LLVMBuildStructGEP(g->builder, expr_val, actual_len_index, "");
+                LLVMValueRef src_len = LLVMBuildLoad(g->builder, src_len_ptr, "");
+                uint64_t src_size = type_size(g, actual_child_type);
+                uint64_t dest_size = type_size(g, wanted_child_type);
+
+                LLVMValueRef new_len;
+                if (dest_size == 1) {
+                    LLVMValueRef src_size_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, src_size, false);
+                    new_len = LLVMBuildMul(g->builder, src_len, src_size_val, "");
+                } else if (src_size == 1) {
+                    LLVMValueRef dest_size_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, dest_size, false);
+                    if (ir_want_debug_safety(g, &cast_instruction->base)) {
+                        LLVMValueRef remainder_val = LLVMBuildURem(g->builder, src_len, dest_size_val, "");
+                        LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_usize->type_ref);
+                        LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, "");
+                        LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "SliceWidenOk");
+                        LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "SliceWidenFail");
+                        LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
+
+                        LLVMPositionBuilderAtEnd(g->builder, fail_block);
+                        gen_debug_safety_crash(g);
+
+                        LLVMPositionBuilderAtEnd(g->builder, ok_block);
+                    }
+                    new_len = ZigLLVMBuildExactUDiv(g->builder, src_len, dest_size_val, "");
+                } else {
+                    zig_unreachable();
+                }
+
+                LLVMValueRef dest_len_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr,
+                        wanted_len_index, "");
+                LLVMBuildStore(g->builder, new_len, dest_len_ptr);
+
+
+                return cast_instruction->tmp_ptr;
+            }
+        case CastOpBytesToSlice:
+            {
+                assert(cast_instruction->tmp_ptr);
+                assert(wanted_type->id == TypeTableEntryIdStruct);
+                assert(wanted_type->data.structure.is_slice);
+                assert(actual_type->id == TypeTableEntryIdArray);
+
+                TypeTableEntry *wanted_pointer_type = wanted_type->data.structure.fields[0].type_entry;
+                TypeTableEntry *wanted_child_type = wanted_pointer_type->data.pointer.child_type;
+
+
+                size_t wanted_ptr_index = wanted_type->data.structure.fields[0].gen_index;
+                LLVMValueRef dest_ptr_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, wanted_ptr_index, "");
+                LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, expr_val, wanted_pointer_type->type_ref, "");
+                LLVMBuildStore(g->builder, src_ptr_casted, dest_ptr_ptr);
+
+                size_t wanted_len_index = wanted_type->data.structure.fields[1].gen_index;
+                LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, wanted_len_index, "");
+                LLVMValueRef len_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref,
+                        actual_type->data.array.len / type_size(g, wanted_child_type), false);
+                LLVMBuildStore(g->builder, len_val, len_ptr);
+
+                return cast_instruction->tmp_ptr;
+            }
+        case CastOpIntToFloat:
+            assert(actual_type->id == TypeTableEntryIdInt);
+            if (actual_type->data.integral.is_signed) {
+                return LLVMBuildSIToFP(g->builder, expr_val, wanted_type->type_ref, "");
+            } else {
+                return LLVMBuildUIToFP(g->builder, expr_val, wanted_type->type_ref, "");
+            }
+        case CastOpFloatToInt:
+            assert(wanted_type->id == TypeTableEntryIdInt);
+            if (wanted_type->data.integral.is_signed) {
+                return LLVMBuildFPToSI(g->builder, expr_val, wanted_type->type_ref, "");
+            } else {
+                return LLVMBuildFPToUI(g->builder, expr_val, wanted_type->type_ref, "");
+            }
+
+        case CastOpBoolToInt:
+            assert(wanted_type->id == TypeTableEntryIdInt);
+            assert(actual_type->id == TypeTableEntryIdBool);
+            return LLVMBuildZExt(g->builder, expr_val, wanted_type->type_ref, "");
+
+        case CastOpIntToEnum:
+            return gen_widen_or_shorten(g, cast_instruction->base.source_node,
+                    actual_type, wanted_type->data.enumeration.tag_type, expr_val);
+        case CastOpEnumToInt:
+            return gen_widen_or_shorten(g, cast_instruction->base.source_node,
+                    actual_type->data.enumeration.tag_type, wanted_type, expr_val);
+    }
+    zig_unreachable();
+}
+
 static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, IrInstruction *instruction) {
     set_debug_source_node(g, instruction->source_node);
     switch (instruction->id) {
@@ -2907,13 +2805,14 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
             return ir_render_load_var(g, executable, (IrInstructionLoadVar *)instruction);
         case IrInstructionIdBinOp:
             return ir_render_bin_op(g, executable, (IrInstructionBinOp *)instruction);
+        case IrInstructionIdCast:
+            return ir_render_cast(g, executable, (IrInstructionCast *)instruction);
         case IrInstructionIdCondBr:
         case IrInstructionIdSwitchBr:
         case IrInstructionIdPhi:
         case IrInstructionIdStoreVar:
         case IrInstructionIdCall:
         case IrInstructionIdBuiltinCall:
-        case IrInstructionIdCast:
             zig_panic("TODO render more IR instructions to LLVM");
     }
     zig_unreachable();
@@ -3592,7 +3491,7 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) {
                                     1, "");
                             LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr,
                                     LLVMPointerType(enum_field->type_entry->type_ref, 0), "");
-                            LLVMValueRef handle_val = get_handle_value(g, var_node, bitcasted_union_field_ptr,
+                            LLVMValueRef handle_val = get_handle_value(g, bitcasted_union_field_ptr,
                                     enum_field->type_entry);
 
                             gen_assign_raw(g, var_node, BinOpTypeAssign,
@@ -3602,8 +3501,7 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) {
                                 // variable is the payload
                                 LLVMValueRef err_payload_ptr = LLVMBuildStructGEP(g->builder,
                                         target_value_handle, 1, "");
-                                LLVMValueRef handle_val = get_handle_value(g, var_node,
-                                        err_payload_ptr, prong_var->type);
+                                LLVMValueRef handle_val = get_handle_value(g, err_payload_ptr, prong_var->type);
                                 gen_assign_raw(g, var_node, BinOpTypeAssign,
                                         prong_var->value_ref, handle_val, prong_var->type, prong_var->type);
                             } else {
@@ -4375,10 +4273,8 @@ static void do_code_gen(CodeGen *g) {
 
         // allocate structs which are the result of casts
         for (size_t cea_i = 0; cea_i < fn_table_entry->cast_alloca_list.length; cea_i += 1) {
-            AstNode *fn_call_node = fn_table_entry->cast_alloca_list.at(cea_i);
-            Expr *expr = &fn_call_node->data.fn_call_expr.resolved_expr;
-            fn_call_node->data.fn_call_expr.tmp_ptr = LLVMBuildAlloca(g->builder,
-                    expr->type_entry->type_ref, "");
+            IrInstructionCast *cast_instruction = fn_table_entry->cast_alloca_list.at(cea_i);
+            cast_instruction->tmp_ptr = LLVMBuildAlloca(g->builder, cast_instruction->base.type_entry->type_ref, "");
         }
 
         // allocate structs which are struct value expressions
src/eval.cpp
@@ -773,6 +773,7 @@ void eval_const_expr_implicit_cast(CastOp cast_op,
         case CastOpEnumToInt:
             bignum_init_unsigned(&const_val->data.x_bignum, other_val->data.x_enum.tag);
             const_val->ok = true;
+            break;
     }
 }
 
src/ir.cpp
@@ -1,6 +1,7 @@
 #include "analyze.hpp"
-#include "ir.hpp"
 #include "error.hpp"
+#include "eval.hpp"
+#include "ir.hpp"
 
 struct IrVarSlot {
     ConstExprValue value;
@@ -100,12 +101,12 @@ static T *ir_build_instruction(IrBuilder *irb, AstNode *source_node) {
 }
 
 static IrInstruction *ir_build_cast(IrBuilder *irb, AstNode *source_node, IrInstruction *dest_type,
-        IrInstruction *value, bool is_implicit)
+        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->is_implicit = is_implicit;
+    cast_instruction->cast_op = cast_op;
     return &cast_instruction->base;
 }
 
@@ -117,6 +118,13 @@ static IrInstruction *ir_build_return(IrBuilder *irb, AstNode *source_node, IrIn
     return &return_instruction->base;
 }
 
+static IrInstruction *ir_build_const(IrBuilder *irb, AstNode *source_node, TypeTableEntry *type_entry) {
+    IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, source_node);
+    const_instruction->base.type_entry = type_entry;
+    const_instruction->base.static_value.ok = true;
+    return &const_instruction->base;
+}
+
 static IrInstruction *ir_build_const_void(IrBuilder *irb, AstNode *source_node) {
     IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, source_node);
     const_instruction->base.type_entry = irb->codegen->builtin_types.entry_void;
@@ -133,14 +141,20 @@ static IrInstruction *ir_build_const_bignum(IrBuilder *irb, AstNode *source_node
     return &const_instruction->base;
 }
 
-static IrInstruction *ir_build_const_type(IrBuilder *irb, AstNode *source_node, TypeTableEntry *type_entry) {
-    IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, source_node);
+static IrInstruction *ir_create_const_type(IrBuilder *irb, AstNode *source_node, TypeTableEntry *type_entry) {
+    IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(irb->exec, source_node);
     const_instruction->base.type_entry = irb->codegen->builtin_types.entry_type;
     const_instruction->base.static_value.ok = true;
     const_instruction->base.static_value.data.x_type = type_entry;
     return &const_instruction->base;
 }
 
+static IrInstruction *ir_build_const_type(IrBuilder *irb, AstNode *source_node, TypeTableEntry *type_entry) {
+    IrInstruction *instruction = ir_create_const_type(irb, source_node, type_entry);
+    ir_instruction_append(irb->current_basic_block, instruction);
+    return instruction;
+}
+
 static IrInstruction *ir_build_const_fn(IrBuilder *irb, AstNode *source_node, FnTableEntry *fn_entry) {
     IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, source_node);
     const_instruction->base.type_entry = fn_entry->type_entry;
@@ -174,6 +188,16 @@ static IrInstruction *ir_build_load_var(IrBuilder *irb, AstNode *source_node, Va
     return &load_var_instruction->base;
 }
 
+static IrInstruction *ir_build_call(IrBuilder *irb, AstNode *source_node,
+        IrInstruction *fn, size_t arg_count, IrInstruction **args)
+{
+    IrInstructionCall *call_instruction = ir_build_instruction<IrInstructionCall>(irb, source_node);
+    call_instruction->fn = fn;
+    call_instruction->arg_count = arg_count;
+    call_instruction->args = args;
+    return &call_instruction->base;
+}
+
 
 //static size_t get_conditional_defer_count(BlockContext *inner_block, BlockContext *outer_block) {
 //    size_t result = 0;
@@ -409,6 +433,26 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, AstNode *node, bool pointer_
     return irb->codegen->invalid_instruction;
 }
 
+static IrInstruction *ir_gen_fn_call(IrBuilder *irb, AstNode *node) {
+    assert(node->type == NodeTypeFnCallExpr);
+
+    if (node->data.fn_call_expr.is_builtin) {
+        zig_panic("TODO ir gen builtin fn");
+    }
+
+    AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr;
+    IrInstruction *fn = ir_gen_node(irb, fn_ref_node, node->block_context);
+
+    size_t arg_count = node->data.fn_call_expr.params.length;
+    IrInstruction **args = allocate<IrInstruction*>(arg_count);
+    for (size_t i = 0; i < arg_count; i += 1) {
+        AstNode *arg_node = node->data.fn_call_expr.params.at(i);
+        args[i] = ir_gen_node(irb, arg_node, node->block_context);
+    }
+
+    return ir_build_call(irb, node, fn, arg_count, args);
+}
+
 static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, BlockContext *block_context,
         bool pointer_only)
 {
@@ -423,12 +467,13 @@ static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, BlockCont
             return ir_gen_num_lit(irb, node);
         case NodeTypeSymbol:
             return ir_gen_symbol(irb, node, pointer_only);
+        case NodeTypeFnCallExpr:
+            return ir_gen_fn_call(irb, node);
         case NodeTypeUnwrapErrorExpr:
         case NodeTypeReturnExpr:
         case NodeTypeDefer:
         case NodeTypeVariableDeclaration:
         case NodeTypePrefixOpExpr:
-        case NodeTypeFnCallExpr:
         case NodeTypeArrayAccessExpr:
         case NodeTypeSliceExpr:
         case NodeTypeFieldAccessExpr:
@@ -782,6 +827,296 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, IrInstruction *pare
     return ir_determine_peer_types(ira, parent_instruction, instructions, instruction_count);
 }
 
+static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value,
+        IrInstruction *dest_type, CastOp cast_op, bool need_alloca)
+{
+    assert(dest_type->type_entry->id == TypeTableEntryIdMetaType);
+    assert(dest_type->static_value.ok);
+    TypeTableEntry *wanted_type = dest_type->static_value.data.x_type;
+
+    if (value->static_value.ok) {
+        IrInstruction *result = ir_build_const(&ira->new_irb, source_instr->source_node, wanted_type);
+        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->other, value->other, cast_op);
+        result->type_entry = wanted_type;
+        if (need_alloca && source_instr->source_node->block_context->fn_entry) {
+            IrInstructionCast *cast_instruction = (IrInstructionCast *)result;
+            source_instr->source_node->block_context->fn_entry->cast_alloca_list.append(cast_instruction);
+        }
+        return result;
+    }
+}
+
+static bool is_slice(TypeTableEntry *type) {
+    return type->id == TypeTableEntryIdStruct && type->data.structure.is_slice;
+}
+
+static bool is_u8(TypeTableEntry *type) {
+    return type->id == TypeTableEntryIdInt &&
+        !type->data.integral.is_signed && type->data.integral.bit_count == 8;
+}
+
+static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr,
+    IrInstruction *dest_type, IrInstruction *value)
+{
+    assert(dest_type->type_entry->id == TypeTableEntryIdMetaType);
+    assert(dest_type->static_value.ok);
+
+    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);
+
+    TypeTableEntry *isize_type = ira->codegen->builtin_types.entry_isize;
+    TypeTableEntry *usize_type = ira->codegen->builtin_types.entry_usize;
+
+    if (wanted_type_canon->id == TypeTableEntryIdInvalid ||
+        actual_type_canon->id == TypeTableEntryIdInvalid)
+    {
+        return ira->codegen->invalid_instruction;
+    }
+
+    // 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);
+    }
+
+    // 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);
+    }
+
+    // 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);
+    }
+
+
+    // explicit cast from isize or usize to pointer
+    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);
+    }
+
+    // explicit widening or shortening cast
+    if ((wanted_type_canon->id == TypeTableEntryIdInt &&
+        actual_type_canon->id == TypeTableEntryIdInt) ||
+        (wanted_type_canon->id == TypeTableEntryIdFloat &&
+        actual_type_canon->id == TypeTableEntryIdFloat))
+    {
+        return ir_resolve_cast(ira, source_instr, value, dest_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);
+    }
+
+    // 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);
+    }
+
+    // explicit cast from array to slice
+    if (is_slice(wanted_type) &&
+        actual_type->id == TypeTableEntryIdArray &&
+        types_match_const_cast_only(
+            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);
+    }
+
+    // explicit cast from []T to []u8 or []u8 to []T
+    if (is_slice(wanted_type) && is_slice(actual_type) &&
+        (is_u8(wanted_type->data.structure.fields[0].type_entry->data.pointer.child_type) ||
+        is_u8(actual_type->data.structure.fields[0].type_entry->data.pointer.child_type)) &&
+        (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);
+    }
+
+    // explicit cast from [N]u8 to []T
+    if (is_slice(wanted_type) &&
+        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);
+        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);
+        } else {
+            add_node_error(ira->codegen, source_instr->source_node,
+                    buf_sprintf("unable to convert %s to %s: size mismatch",
+                        buf_ptr(&actual_type->name), buf_ptr(&wanted_type->name)));
+            return ira->codegen->invalid_instruction;
+        }
+    }
+
+    // explicit cast from pointer to another pointer
+    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);
+    }
+
+    // explicit cast from maybe pointer to another maybe pointer
+    if (actual_type->id == TypeTableEntryIdMaybe &&
+        (actual_type->data.maybe.child_type->id == TypeTableEntryIdPointer ||
+            actual_type->data.maybe.child_type->id == TypeTableEntryIdFn) &&
+        wanted_type->id == TypeTableEntryIdMaybe &&
+        (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);
+    }
+
+    // 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,
+                    CastOpMaybeWrap, true);
+            cast_instruction->return_knowledge = ReturnKnowledgeKnownNonNull;
+            return cast_instruction;
+        } else if (actual_type->id == TypeTableEntryIdNumLitInt ||
+                   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,
+                        CastOpMaybeWrap, true);
+                cast_instruction->return_knowledge = ReturnKnowledgeKnownNonNull;
+                return cast_instruction;
+            } else {
+                return ira->codegen->invalid_instruction;
+            }
+        }
+    }
+
+    // explicit cast from null literal to maybe type
+    if (wanted_type->id == TypeTableEntryIdMaybe &&
+        actual_type->id == TypeTableEntryIdNullLit)
+    {
+        IrInstruction *cast_instruction = ir_resolve_cast(ira, source_instr, value, dest_type,
+                CastOpNullToMaybe, true);
+        cast_instruction->return_knowledge = ReturnKnowledgeKnownNull;
+        return cast_instruction;
+    }
+
+    // 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,
+                    CastOpErrorWrap, true);
+            cast_instruction->return_knowledge = ReturnKnowledgeKnownNonError;
+            return cast_instruction;
+        } else if (actual_type->id == TypeTableEntryIdNumLitInt ||
+                   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,
+                        CastOpErrorWrap, true);
+                cast_instruction->return_knowledge = ReturnKnowledgeKnownNonError;
+                return cast_instruction;
+            } else {
+                return ira->codegen->invalid_instruction;
+            }
+        }
+    }
+
+    // explicit cast from pure error to error union type
+    if (wanted_type->id == TypeTableEntryIdErrorUnion &&
+        actual_type->id == TypeTableEntryIdPureError)
+    {
+        IrInstruction *cast_instruction = ir_resolve_cast(ira, source_instr, value, dest_type,
+                CastOpPureErrorWrap, false);
+        cast_instruction->return_knowledge = ReturnKnowledgeKnownError;
+        return cast_instruction;
+    }
+
+    // explicit cast from number literal to another type
+    if (actual_type->id == TypeTableEntryIdNumLitFloat ||
+        actual_type->id == TypeTableEntryIdNumLitInt)
+    {
+        if (ir_num_lit_fits_in_other_type(ira, value, wanted_type_canon)) {
+            CastOp op;
+            if ((actual_type->id == TypeTableEntryIdNumLitFloat &&
+                 wanted_type_canon->id == TypeTableEntryIdFloat) ||
+                (actual_type->id == TypeTableEntryIdNumLitInt &&
+                 wanted_type_canon->id == TypeTableEntryIdInt))
+            {
+                op = CastOpNoop;
+            } else if (wanted_type_canon->id == TypeTableEntryIdInt) {
+                op = CastOpFloatToInt;
+            } else if (wanted_type_canon->id == TypeTableEntryIdFloat) {
+                op = CastOpIntToFloat;
+            } else {
+                zig_unreachable();
+            }
+            return ir_resolve_cast(ira, source_instr, value, dest_type, op, false);
+        } else {
+            return ira->codegen->invalid_instruction;
+        }
+    }
+
+    // explicit cast from %void to integer type which can fit it
+    bool actual_type_is_void_err = actual_type->id == TypeTableEntryIdErrorUnion &&
+        !type_has_bits(actual_type->data.error.child_type);
+    bool actual_type_is_pure_err = actual_type->id == TypeTableEntryIdPureError;
+    if ((actual_type_is_void_err || actual_type_is_pure_err) &&
+        wanted_type->id == TypeTableEntryIdInt)
+    {
+        BigNum bn;
+        bignum_init_unsigned(&bn, ira->codegen->error_decls.length);
+        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);
+        } 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)));
+            return ira->codegen->invalid_instruction;
+        }
+    }
+
+    // explicit cast from integer to enum type with no payload
+    if (actual_type->id == TypeTableEntryIdInt &&
+        wanted_type->id == TypeTableEntryIdEnum &&
+        wanted_type->data.enumeration.gen_field_count == 0)
+    {
+        return ir_resolve_cast(ira, source_instr, value, dest_type, CastOpIntToEnum, false);
+    }
+
+    // explicit cast from enum type with no payload to integer
+    if (wanted_type->id == TypeTableEntryIdInt &&
+        actual_type->id == TypeTableEntryIdEnum &&
+        actual_type->data.enumeration.gen_field_count == 0)
+    {
+        return ir_resolve_cast(ira, source_instr, value, dest_type, CastOpEnumToInt, false);
+    }
+
+    add_node_error(ira->codegen, source_instr->source_node,
+        buf_sprintf("invalid cast from type '%s' to '%s'",
+            buf_ptr(&actual_type->name),
+            buf_ptr(&wanted_type->name)));
+    return ira->codegen->invalid_instruction;
+}
+
 static IrInstruction *ir_get_casted_value(IrAnalyze *ira, IrInstruction *value, TypeTableEntry *expected_type) {
     assert(value);
     assert(value != ira->old_irb.codegen->invalid_instruction);
@@ -806,10 +1141,8 @@ static IrInstruction *ir_get_casted_value(IrAnalyze *ira, IrInstruction *value,
 
         case ImplicitCastMatchResultYes:
             {
-                IrInstruction *dest_type = ir_build_const_type(&ira->new_irb, value->source_node, expected_type);
-                bool is_implicit = true;
-                IrInstruction *cast_instruction = ir_build_cast(&ira->new_irb, value->source_node, dest_type,
-                        value, is_implicit);
+                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;
             }
         case ImplicitCastMatchResultReportedError:
@@ -849,18 +1182,41 @@ static TypeTableEntry *ir_analyze_bin_op_bool(IrAnalyze *ira, IrInstructionBinOp
     IrInstruction *op1 = bin_op_instruction->op1;
     IrInstruction *op2 = bin_op_instruction->op2;
 
-    IrInstruction *casted_op1 = ir_get_casted_value(ira, op1->other, ira->old_irb.codegen->builtin_types.entry_bool);
+    TypeTableEntry *bool_type = ira->old_irb.codegen->builtin_types.entry_bool;
+
+    IrInstruction *casted_op1 = ir_get_casted_value(ira, op1->other, bool_type);
     if (casted_op1 == ira->old_irb.codegen->invalid_instruction)
         return ira->old_irb.codegen->builtin_types.entry_invalid;
 
-    IrInstruction *casted_op2 = ir_get_casted_value(ira, op2->other, ira->old_irb.codegen->builtin_types.entry_bool);
+    IrInstruction *casted_op2 = ir_get_casted_value(ira, op2->other, bool_type);
     if (casted_op2 == ira->old_irb.codegen->invalid_instruction)
         return ira->old_irb.codegen->builtin_types.entry_invalid;
 
+    ConstExprValue *op1_val = &casted_op1->static_value;
+    ConstExprValue *op2_val = &casted_op2->static_value;
+    if (op1_val->ok && op2_val->ok) {
+        ConstExprValue *out_val = &bin_op_instruction->base.static_value;
+        bin_op_instruction->base.other = &bin_op_instruction->base;
+
+        assert(op1->type_entry->id == TypeTableEntryIdBool);
+        assert(op2->type_entry->id == TypeTableEntryIdBool);
+        if (bin_op_instruction->op_id == IrBinOpBoolOr) {
+            out_val->data.x_bool = op1_val->data.x_bool || op2_val->data.x_bool;
+        } else if (bin_op_instruction->op_id == IrBinOpBoolAnd) {
+            out_val->data.x_bool = op1_val->data.x_bool && op2_val->data.x_bool;
+        } else {
+            zig_unreachable();
+        }
+        out_val->ok = true;
+        out_val->depends_on_compile_var = op1_val->depends_on_compile_var ||
+            op2_val->depends_on_compile_var;
+        return bool_type;
+    }
+
     ir_link_new(ir_build_bin_op(&ira->new_irb, bin_op_instruction->base.source_node,
                 bin_op_instruction->op_id, op1->other, op2->other), &bin_op_instruction->base);
 
-    return ira->old_irb.codegen->builtin_types.entry_bool;
+    return bool_type;
 }
 
 static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
@@ -925,12 +1281,109 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
             zig_unreachable();
     }
 
+    zig_panic("TODO interpret bin_op_cmp");
+
     ir_link_new(ir_build_bin_op(&ira->new_irb, bin_op_instruction->base.source_node,
                 op_id, op1->other, op2->other), &bin_op_instruction->base);
 
     return ira->old_irb.codegen->builtin_types.entry_bool;
 }
 
+static uint64_t max_unsigned_val(TypeTableEntry *type_entry) {
+    assert(type_entry->id == TypeTableEntryIdInt);
+    if (type_entry->data.integral.bit_count == 64) {
+        return UINT64_MAX;
+    } else if (type_entry->data.integral.bit_count == 32) {
+        return UINT32_MAX;
+    } else if (type_entry->data.integral.bit_count == 16) {
+        return UINT16_MAX;
+    } else if (type_entry->data.integral.bit_count == 8) {
+        return UINT8_MAX;
+    } else {
+        zig_unreachable();
+    }
+}
+
+static int ir_eval_bignum(ConstExprValue *op1_val, ConstExprValue *op2_val,
+        ConstExprValue *out_val, bool (*bignum_fn)(BigNum *, BigNum *, BigNum *),
+        TypeTableEntry *type, bool wrapping_op)
+{
+    bool overflow = bignum_fn(&out_val->data.x_bignum, &op1_val->data.x_bignum, &op2_val->data.x_bignum);
+    if (overflow) {
+        return ErrorOverflow;
+    }
+
+    if (type->id == TypeTableEntryIdInt && !bignum_fits_in_bits(&out_val->data.x_bignum,
+                type->data.integral.bit_count, type->data.integral.is_signed))
+    {
+        if (wrapping_op) {
+            if (type->data.integral.is_signed) {
+                out_val->data.x_bignum.data.x_uint = max_unsigned_val(type) - out_val->data.x_bignum.data.x_uint + 1;
+                out_val->data.x_bignum.is_negative = !out_val->data.x_bignum.is_negative;
+            } else if (out_val->data.x_bignum.is_negative) {
+                out_val->data.x_bignum.data.x_uint = max_unsigned_val(type) - out_val->data.x_bignum.data.x_uint + 1;
+                out_val->data.x_bignum.is_negative = false;
+            } else {
+                bignum_truncate(&out_val->data.x_bignum, type->data.integral.bit_count);
+            }
+        } else {
+            return ErrorOverflow;
+        }
+    }
+
+    out_val->ok = true;
+    out_val->depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var;
+    return 0;
+}
+
+static int ir_eval_math_op(ConstExprValue *op1_val, TypeTableEntry *op1_type,
+        IrBinOp op_id, ConstExprValue *op2_val, TypeTableEntry *op2_type, ConstExprValue *out_val)
+{
+    switch (op_id) {
+        case IrBinOpInvalid:
+        case IrBinOpBoolOr:
+        case IrBinOpBoolAnd:
+        case IrBinOpCmpEq:
+        case IrBinOpCmpNotEq:
+        case IrBinOpCmpLessThan:
+        case IrBinOpCmpGreaterThan:
+        case IrBinOpCmpLessOrEq:
+        case IrBinOpCmpGreaterOrEq:
+        case IrBinOpArrayCat:
+        case IrBinOpArrayMult:
+            zig_unreachable();
+        case IrBinOpBinOr:
+            return ir_eval_bignum(op1_val, op2_val, out_val, bignum_or, op1_type, false);
+        case IrBinOpBinXor:
+            return ir_eval_bignum(op1_val, op2_val, out_val, bignum_xor, op1_type, false);
+        case IrBinOpBinAnd:
+            return ir_eval_bignum(op1_val, op2_val, out_val, bignum_and, op1_type, false);
+        case IrBinOpBitShiftLeft:
+            return ir_eval_bignum(op1_val, op2_val, out_val, bignum_shl, op1_type, false);
+        case IrBinOpBitShiftLeftWrap:
+            return ir_eval_bignum(op1_val, op2_val, out_val, bignum_shl, op1_type, true);
+        case IrBinOpBitShiftRight:
+            return ir_eval_bignum(op1_val, op2_val, out_val, bignum_shr, op1_type, false);
+        case IrBinOpAdd:
+            return ir_eval_bignum(op1_val, op2_val, out_val, bignum_add, op1_type, false);
+        case IrBinOpAddWrap:
+            return ir_eval_bignum(op1_val, op2_val, out_val, bignum_add, op1_type, true);
+        case IrBinOpSub:
+            return ir_eval_bignum(op1_val, op2_val, out_val, bignum_sub, op1_type, false);
+        case IrBinOpSubWrap:
+            return ir_eval_bignum(op1_val, op2_val, out_val, bignum_sub, op1_type, true);
+        case IrBinOpMult:
+            return ir_eval_bignum(op1_val, op2_val, out_val, bignum_mul, op1_type, false);
+        case IrBinOpMultWrap:
+            return ir_eval_bignum(op1_val, op2_val, out_val, bignum_mul, op1_type, true);
+        case IrBinOpDiv:
+            return ir_eval_bignum(op1_val, op2_val, out_val, bignum_div, op1_type, false);
+        case IrBinOpMod:
+            return ir_eval_bignum(op1_val, op2_val, out_val, bignum_mod, op1_type, false);
+    }
+    zig_unreachable();
+}
+
 static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
     IrInstruction *op1 = bin_op_instruction->op1;
     IrInstruction *op2 = bin_op_instruction->op2;
@@ -955,12 +1408,39 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
         // float
     } else {
         AstNode *source_node = bin_op_instruction->base.source_node;
-        add_node_error(ira->old_irb.codegen, source_node, buf_sprintf("invalid operands to binary expression: '%s' and '%s'",
+        add_node_error(ira->old_irb.codegen, source_node,
+            buf_sprintf("invalid operands to binary expression: '%s' and '%s'",
                 buf_ptr(&op1->type_entry->name),
                 buf_ptr(&op2->type_entry->name)));
         return ira->old_irb.codegen->builtin_types.entry_invalid;
     }
 
+    if (op1->static_value.ok && op2->static_value.ok) {
+        ConstExprValue *op1_val = &op1->static_value;
+        ConstExprValue *op2_val = &op2->static_value;
+        ConstExprValue *out_val = &bin_op_instruction->base.static_value;
+
+        bin_op_instruction->base.other = &bin_op_instruction->base;
+
+        int err;
+        if ((err = ir_eval_math_op(op1_val, resolved_type, op_id, op2_val, resolved_type, out_val))) {
+            if (err == ErrorDivByZero) {
+                add_node_error(ira->codegen, bin_op_instruction->base.source_node,
+                        buf_sprintf("division by zero is undefined"));
+                return ira->codegen->builtin_types.entry_invalid;
+            } else if (err == ErrorOverflow) {
+                add_node_error(ira->codegen, bin_op_instruction->base.source_node,
+                        buf_sprintf("value cannot be represented in any integer type"));
+                return ira->codegen->builtin_types.entry_invalid;
+            }
+            return ira->codegen->builtin_types.entry_invalid;
+        }
+
+        ir_num_lit_fits_in_other_type(ira, &bin_op_instruction->base, resolved_type);
+        return resolved_type;
+
+    }
+
     ir_link_new(ir_build_bin_op(&ira->new_irb, bin_op_instruction->base.source_node,
             op_id, op1->other, op2->other), &bin_op_instruction->base);
 
@@ -1011,6 +1491,40 @@ static TypeTableEntry *ir_analyze_instruction_load_var(IrAnalyze *ira, IrInstruc
     return load_var_instruction->var->type;
 }
 
+static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstructionCall *call_instruction) {
+    IrInstruction *fn_ref = call_instruction->fn->other;
+    if (fn_ref->type_entry->id == TypeTableEntryIdInvalid)
+        return ira->codegen->builtin_types.entry_invalid;
+
+    if (fn_ref->static_value.ok) {
+        if (fn_ref->type_entry->id == TypeTableEntryIdMetaType) {
+            size_t actual_param_count = call_instruction->arg_count;
+
+            if (actual_param_count != 1) {
+                add_node_error(ira->codegen, call_instruction->base.source_node,
+                        buf_sprintf("cast expression expects exactly one parameter"));
+                return ira->codegen->builtin_types.entry_invalid;
+            }
+
+            IrInstruction *arg = call_instruction->args[0];
+            IrInstruction *cast_instruction = ir_analyze_cast(ira, &call_instruction->base, fn_ref, arg);
+            if (cast_instruction == ira->codegen->invalid_instruction)
+                return ira->codegen->builtin_types.entry_invalid;
+
+            ir_link_new(cast_instruction, &call_instruction->base);
+            return cast_instruction->type_entry;
+        } else {
+            zig_panic("TODO analyze more fn call types");
+        }
+    } else {
+        //ir_link_new(ir_build_call(&ira->new_irb, call_instruction->base.source_node,
+        //        call_instruction->fn, call_instruction->arg_count, call_instruction->args),
+        //        &call_instruction->base);
+
+        zig_panic("TODO analyze fn call");
+    }
+}
+
 static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
     switch (instruction->id) {
         case IrInstructionIdInvalid:
@@ -1023,11 +1537,12 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
             return ir_analyze_instruction_bin_op(ira, (IrInstructionBinOp *)instruction);
         case IrInstructionIdLoadVar:
             return ir_analyze_instruction_load_var(ira, (IrInstructionLoadVar *)instruction);
+        case IrInstructionIdCall:
+            return ir_analyze_instruction_call(ira, (IrInstructionCall *)instruction);
         case IrInstructionIdCondBr:
         case IrInstructionIdSwitchBr:
         case IrInstructionIdPhi:
         case IrInstructionIdStoreVar:
-        case IrInstructionIdCall:
         case IrInstructionIdBuiltinCall:
         case IrInstructionIdCast:
             zig_panic("TODO analyze more instructions");
src/ir_print.cpp
@@ -20,7 +20,7 @@ static void ir_print_prefix(IrPrint *irp, IrInstruction *instruction) {
 static void ir_print_return(IrPrint *irp, IrInstructionReturn *return_instruction) {
     ir_print_prefix(irp, &return_instruction->base);
     assert(return_instruction->value);
-    fprintf(irp->f, "return #%zu;\n", return_instruction->value->debug_id);
+    fprintf(irp->f, "return #%zu\n", return_instruction->value->debug_id);
 }
 
 static void ir_print_const(IrPrint *irp, IrInstructionConst *const_instruction) {
@@ -43,8 +43,10 @@ static void ir_print_const(IrPrint *irp, IrInstructionConst *const_instruction)
                 fprintf(irp->f, "%s%llu\n", negative_str, bignum->data.x_uint);
                 break;
             }
-        case TypeTableEntryIdVar:
         case TypeTableEntryIdMetaType:
+            fprintf(irp->f, "%s\n", buf_ptr(&const_instruction->base.static_value.data.x_type->name));
+            break;
+        case TypeTableEntryIdVar:
         case TypeTableEntryIdBool:
         case TypeTableEntryIdUnreachable:
         case TypeTableEntryIdInt:
@@ -139,6 +141,25 @@ static void ir_print_load_var(IrPrint *irp, IrInstructionLoadVar *load_var_instr
             buf_ptr(&load_var_instruction->var->name));
 }
 
+static void ir_print_cast(IrPrint *irp, IrInstructionCast *cast_instruction) {
+    ir_print_prefix(irp, &cast_instruction->base);
+    fprintf(irp->f, "cast #%zu to #%zu\n",
+            cast_instruction->value->debug_id,
+            cast_instruction->dest_type->debug_id);
+}
+
+static void ir_print_call(IrPrint *irp, IrInstructionCall *call_instruction) {
+    ir_print_prefix(irp, &call_instruction->base);
+    fprintf(irp->f, "#%zu(", call_instruction->fn->debug_id);
+    for (size_t i = 0; i < call_instruction->arg_count; i += 1) {
+        IrInstruction *arg = call_instruction->args[i];
+        if (i != 0)
+            fprintf(irp->f, ", ");
+        fprintf(irp->f, "#%zu", arg->debug_id);
+    }
+    fprintf(irp->f, ")\n");
+}
+
 static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
     switch (instruction->id) {
         case IrInstructionIdInvalid:
@@ -155,13 +176,17 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
         case IrInstructionIdLoadVar:
             ir_print_load_var(irp, (IrInstructionLoadVar *)instruction);
             break;
+        case IrInstructionIdCast:
+            ir_print_cast(irp, (IrInstructionCast *)instruction);
+            break;
+        case IrInstructionIdCall:
+            ir_print_call(irp, (IrInstructionCall *)instruction);
+            break;
         case IrInstructionIdCondBr:
         case IrInstructionIdSwitchBr:
         case IrInstructionIdPhi:
         case IrInstructionIdStoreVar:
-        case IrInstructionIdCall:
         case IrInstructionIdBuiltinCall:
-        case IrInstructionIdCast:
             zig_panic("TODO print more IR instructions");
     }
 }