Commit 6caf32195a

Andrew Kelley <superjoe30@gmail.com>
2017-01-09 04:25:38
pass unnecessary if statement test
1 parent 76d0e49
doc/langref.md
@@ -540,9 +540,9 @@ variables:
  * "is_big_endian" `bool` - either `true` for big endian or `false` for little endian.
  * "is_release" `bool`- either `true` for release mode builds or `false` for debug mode builds.
  * "is_test" `bool`- either `true` for test builds or `false` otherwise.
- * "os" `@OS` - use `zig targets` to see what enum values are possible here.
- * "arch" `@Arch` - use `zig targets` to see what enum values are possible here.
- * "environ" `@Environ` - use `zig targets` to see what enum values are possible here.
+ * "os" `Os` - use `zig targets` to see what enum values are possible here.
+ * "arch" `Arch` - use `zig targets` to see what enum values are possible here.
+ * "environ" `Environ` - use `zig targets` to see what enum values are possible here.
 
 Build scripts can set additional compile variables of any name and type.
 
@@ -556,6 +556,18 @@ expression is not known at compile time.
 
 The result of the function is the result of the expression.
 
+### @generatedCode(expression) -> @typeOf(expression)
+
+This function wraps an expression and returns the result of the expression
+unmodified.
+
+Inside the expression, code is considered generated, which means that the
+following compile errors are disabled:
+
+ * unnecessary if statement error
+
+The result of the expression is marked as depending on a compile variable.
+
 ### @ctz(x: T) -> T
 
 This function counts the number of trailing zeroes in x which is an integer
@@ -638,3 +650,4 @@ This function returns an integer type with the given signness and bit count.
 ### @setFnTest(func)
 
 Makes the target function a test function.
+
src/all_types.hpp
@@ -942,6 +942,7 @@ struct TypeTableEntry {
     ZigLLVMDIType *di_type;
 
     bool zero_bits;
+    bool size_depends_on_compile_var;
 
     union {
         TypeTableEntryPointer pointer;
@@ -1053,6 +1054,7 @@ enum BuiltinFnId {
     BuiltinFnIdCompileVar,
     BuiltinFnIdCompileErr,
     BuiltinFnIdStaticEval,
+    BuiltinFnIdGeneratedCode,
     BuiltinFnIdCtz,
     BuiltinFnIdClz,
     BuiltinFnIdImport,
@@ -1440,6 +1442,7 @@ enum IrInstructionId {
     IrInstructionIdClz,
     IrInstructionIdCtz,
     IrInstructionIdStaticEval,
+    IrInstructionIdGeneratedCode,
     IrInstructionIdImport,
     IrInstructionIdCImport,
     IrInstructionIdCInclude,
@@ -1856,6 +1859,12 @@ struct IrInstructionStaticEval {
     IrInstruction *value;
 };
 
+struct IrInstructionGeneratedCode {
+    IrInstruction base;
+
+    IrInstruction *value;
+};
+
 struct IrInstructionImport {
     IrInstruction base;
 
src/analyze.cpp
@@ -1148,6 +1148,9 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) {
             continue;
         }
 
+        enum_type->size_depends_on_compile_var = enum_type->size_depends_on_compile_var ||
+            field_type->size_depends_on_compile_var;
+
         if (!type_has_bits(field_type))
             continue;
 
@@ -1316,6 +1319,9 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) {
             continue;
         }
 
+        struct_type->size_depends_on_compile_var = struct_type->size_depends_on_compile_var ||
+            field_type->size_depends_on_compile_var;
+
         if (!type_has_bits(field_type))
             continue;
 
src/codegen.cpp
@@ -2273,6 +2273,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
         case IrInstructionIdAlignOf:
         case IrInstructionIdFnProto:
         case IrInstructionIdTestComptime:
+        case IrInstructionIdGeneratedCode:
             zig_unreachable();
         case IrInstructionIdReturn:
             return ir_render_return(g, executable, (IrInstructionReturn *)instruction);
@@ -3201,6 +3202,7 @@ static void define_builtin_types(CodeGen *g) {
         bool is_signed = info->is_signed;
 
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
+        entry->size_depends_on_compile_var = true;
         entry->type_ref = LLVMIntType(size_in_bits);
 
         buf_init_from_str(&entry->name, info->name);
@@ -3237,6 +3239,7 @@ static void define_builtin_types(CodeGen *g) {
 
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
         entry->type_ref = LLVMIntType(g->pointer_size_bytes * 8);
+        entry->size_depends_on_compile_var = true;
 
         const char u_or_i = is_signed ? 'i' : 'u';
         buf_resize(&entry->name, 0);
@@ -3292,6 +3295,7 @@ static void define_builtin_types(CodeGen *g) {
     {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat);
         entry->type_ref = LLVMX86FP80Type();
+        entry->size_depends_on_compile_var = true;
         buf_init_from_str(&entry->name, "c_long_double");
         entry->data.floating.bit_count = 80;
 
@@ -3613,6 +3617,7 @@ static void define_builtin_fns(CodeGen *g) {
     create_builtin_fn(g, BuiltinFnIdCUndef, "cUndef", 1);
     create_builtin_fn(g, BuiltinFnIdCompileVar, "compileVar", 1);
     create_builtin_fn(g, BuiltinFnIdStaticEval, "staticEval", 1);
+    create_builtin_fn(g, BuiltinFnIdGeneratedCode, "generatedCode", 1);
     create_builtin_fn(g, BuiltinFnIdCtz, "ctz", 1);
     create_builtin_fn(g, BuiltinFnIdClz, "clz", 1);
     create_builtin_fn(g, BuiltinFnIdImport, "import", 1);
src/ir.cpp
@@ -307,6 +307,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionStaticEval *) {
     return IrInstructionIdStaticEval;
 }
 
+static constexpr IrInstructionId ir_instruction_id(IrInstructionGeneratedCode *) {
+    return IrInstructionIdGeneratedCode;
+}
+
 static constexpr IrInstructionId ir_instruction_id(IrInstructionImport *) {
     return IrInstructionIdImport;
 }
@@ -616,19 +620,20 @@ static IrInstruction *ir_build_const_usize(IrBuilder *irb, Scope *scope, AstNode
 }
 
 static IrInstruction *ir_create_const_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
-        TypeTableEntry *type_entry)
+        TypeTableEntry *type_entry, bool depends_on_compile_var)
 {
     IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(irb, scope, source_node);
     const_instruction->base.value.type = irb->codegen->builtin_types.entry_type;
     const_instruction->base.value.special = ConstValSpecialStatic;
+    const_instruction->base.value.depends_on_compile_var = depends_on_compile_var;
     const_instruction->base.value.data.x_type = type_entry;
     return &const_instruction->base;
 }
 
 static IrInstruction *ir_build_const_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
-        TypeTableEntry *type_entry)
+        TypeTableEntry *type_entry, bool depends_on_compile_var)
 {
-    IrInstruction *instruction = ir_create_const_type(irb, scope, source_node, type_entry);
+    IrInstruction *instruction = ir_create_const_type(irb, scope, source_node, type_entry, depends_on_compile_var);
     ir_instruction_append(irb->current_basic_block, instruction);
     return instruction;
 }
@@ -1392,6 +1397,17 @@ static IrInstruction *ir_build_static_eval(IrBuilder *irb, Scope *scope, AstNode
     return &instruction->base;
 }
 
+static IrInstruction *ir_build_generated_code(IrBuilder *irb, Scope *scope, AstNode *source_node,
+        IrInstruction *value)
+{
+    IrInstructionGeneratedCode *instruction = ir_build_instruction<IrInstructionGeneratedCode>(irb, scope, source_node);
+    instruction->value = value;
+
+    ir_ref_instruction(value, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
 static IrInstruction *ir_build_import(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *name) {
     IrInstructionImport *instruction = ir_build_instruction<IrInstructionImport>(irb, scope, source_node);
     instruction->name = name;
@@ -2258,6 +2274,13 @@ static IrInstruction *ir_instruction_staticeval_get_dep(IrInstructionStaticEval
     }
 }
 
+static IrInstruction *ir_instruction_generatedcode_get_dep(IrInstructionGeneratedCode *instruction, size_t index) {
+    switch (index) {
+        case 0: return instruction->value;
+        default: return nullptr;
+    }
+}
+
 static IrInstruction *ir_instruction_import_get_dep(IrInstructionImport *instruction, size_t index) {
     switch (index) {
         case 0: return instruction->name;
@@ -2645,6 +2668,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t
             return ir_instruction_ctz_get_dep((IrInstructionCtz *) instruction, index);
         case IrInstructionIdStaticEval:
             return ir_instruction_staticeval_get_dep((IrInstructionStaticEval *) instruction, index);
+        case IrInstructionIdGeneratedCode:
+            return ir_instruction_generatedcode_get_dep((IrInstructionGeneratedCode *) instruction, index);
         case IrInstructionIdImport:
             return ir_instruction_import_get_dep((IrInstructionImport *) instruction, index);
         case IrInstructionIdCImport:
@@ -2846,7 +2871,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
                         is_comptime = ir_build_test_comptime(irb, scope, node, is_err);
                     }
 
-                    ir_build_cond_br(irb, scope, node, is_err, err_block, ok_block, is_comptime);
+                    ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err, err_block, ok_block, is_comptime));
 
                     ir_set_cursor_at_end(irb, err_block);
                     ir_gen_defers_for_block(irb, scope, outer_scope, true, false);
@@ -2868,7 +2893,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
                         is_comptime = ir_build_test_comptime(irb, scope, node, is_non_null);
                     }
 
-                    ir_build_cond_br(irb, scope, node, is_non_null, ok_block, null_block, is_comptime);
+                    ir_mark_gen(ir_build_cond_br(irb, scope, node, is_non_null, ok_block, null_block, is_comptime));
 
                     ir_set_cursor_at_end(irb, null_block);
                     ir_gen_defers_for_block(irb, scope, outer_scope, false, true);
@@ -2895,7 +2920,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
                 IrBasicBlock *return_block = ir_build_basic_block(irb, scope, "ErrRetReturn");
                 IrBasicBlock *continue_block = ir_build_basic_block(irb, scope, "ErrRetContinue");
                 IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, ir_should_inline(irb));
-                ir_build_cond_br(irb, scope, node, is_err_val, return_block, continue_block, is_comptime);
+                ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err_val, return_block, continue_block, is_comptime));
 
                 ir_set_cursor_at_end(irb, return_block);
                 ir_gen_defers_for_block(irb, scope, outer_scope, true, false);
@@ -2921,7 +2946,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
                 IrBasicBlock *return_block = ir_build_basic_block(irb, scope, "MaybeRetReturn");
                 IrBasicBlock *continue_block = ir_build_basic_block(irb, scope, "MaybeRetContinue");
                 IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, ir_should_inline(irb));
-                ir_build_cond_br(irb, scope, node, is_non_null, continue_block, return_block, is_comptime);
+                ir_mark_gen(ir_build_cond_br(irb, scope, node, is_non_null, continue_block, return_block, is_comptime));
 
                 ir_set_cursor_at_end(irb, return_block);
                 ir_gen_defers_for_block(irb, scope, outer_scope, false, true);
@@ -3388,7 +3413,7 @@ static IrInstruction *ir_gen_null_literal(IrBuilder *irb, Scope *scope, AstNode
 static IrInstruction *ir_gen_var_literal(IrBuilder *irb, Scope *scope, AstNode *node) {
     assert(node->type == NodeTypeVarLiteral);
 
-    return ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_var);
+    return ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_var, false);
 }
 
 static IrInstruction *ir_gen_decl_ref(IrBuilder *irb, AstNode *source_node, Tld *tld,
@@ -3426,7 +3451,7 @@ static IrInstruction *ir_gen_decl_ref(IrBuilder *irb, AstNode *source_node, Tld
         {
             TldTypeDef *tld_typedef = (TldTypeDef *)tld;
             TypeTableEntry *typedef_type = tld_typedef->type_entry;
-            IrInstruction *ref_instruction = ir_build_const_type(irb, scope, source_node, typedef_type);
+            IrInstruction *ref_instruction = ir_build_const_type(irb, scope, source_node, typedef_type, false);
             if (lval != LValPurposeNone)
                 return ir_build_ref(irb, scope, source_node, ref_instruction, true);
             else
@@ -3443,7 +3468,7 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node,
 
     auto primitive_table_entry = irb->codegen->primitive_type_table.maybe_get(variable_name);
     if (primitive_table_entry) {
-        IrInstruction *value = ir_build_const_type(irb, scope, node, primitive_table_entry->value);
+        IrInstruction *value = ir_build_const_type(irb, scope, node, primitive_table_entry->value, false);
         if (lval != LValPurposeNone) {
             return ir_build_ref(irb, scope, node, value, lval == LValPurposeAddressOfConst);
         } else {
@@ -3664,6 +3689,15 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
 
                 return ir_build_static_eval(irb, scope, node, arg0_value);
             }
+        case BuiltinFnIdGeneratedCode:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_instruction)
+                    return arg0_value;
+
+                return ir_build_generated_code(irb, scope, node, arg0_value);
+            }
         case BuiltinFnIdImport:
             {
                 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
@@ -4327,7 +4361,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo
     }
     child_scope = index_var->child_scope;
 
-    IrInstruction *usize = ir_build_const_type(irb, child_scope, node, irb->codegen->builtin_types.entry_usize);
+    IrInstruction *usize = ir_build_const_type(irb, child_scope, node, irb->codegen->builtin_types.entry_usize, false);
     IrInstruction *zero = ir_build_const_usize(irb, child_scope, node, 0);
     IrInstruction *one = ir_build_const_usize(irb, child_scope, node, 1);
     ir_build_var_decl(irb, child_scope, index_var_source_node, index_var, usize, zero);
@@ -4345,7 +4379,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo
     ir_set_cursor_at_end(irb, cond_block);
     IrInstruction *index_val = ir_build_load_ptr(irb, child_scope, node, index_ptr);
     IrInstruction *cond = ir_build_bin_op(irb, child_scope, node, IrBinOpCmpLessThan, index_val, len_val, false);
-    ir_build_cond_br(irb, child_scope, node, cond, body_block, end_block, is_comptime);
+    ir_mark_gen(ir_build_cond_br(irb, child_scope, node, cond, body_block, end_block, is_comptime));
 
     ir_set_cursor_at_end(irb, body_block);
     IrInstruction *elem_ptr = ir_build_elem_ptr(irb, child_scope, node, array_val_ptr, index_val, false);
@@ -4391,7 +4425,7 @@ static IrInstruction *ir_gen_this_literal(IrBuilder *irb, Scope *scope, AstNode
         ScopeDecls *decls_scope = (ScopeDecls *)scope;
         TypeTableEntry *container_type = decls_scope->container_type;
         assert(container_type);
-        return ir_build_const_type(irb, scope, node, container_type);
+        return ir_build_const_type(irb, scope, node, container_type, false);
     }
 
     if (scope->id == ScopeIdBlock)
@@ -4719,7 +4753,8 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *
 
                 assert(ok_bit);
                 assert(last_item_node);
-                ir_build_cond_br(irb, scope, last_item_node, ok_bit, range_block_yes, range_block_no, is_comptime);
+                ir_mark_gen(ir_build_cond_br(irb, scope, last_item_node, ok_bit, range_block_yes,
+                            range_block_no, is_comptime));
 
                 ir_set_cursor_at_end(irb, range_block_yes);
                 if (!ir_gen_switch_prong_expr(irb, scope, node, prong_node, end_block,
@@ -4843,12 +4878,12 @@ static IrInstruction *ir_gen_continue(IrBuilder *irb, Scope *scope, AstNode *nod
 
 static IrInstruction *ir_gen_type_literal(IrBuilder *irb, Scope *scope, AstNode *node) {
     assert(node->type == NodeTypeTypeLiteral);
-    return ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_type);
+    return ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_type, false);
 }
 
 static IrInstruction *ir_gen_error_type(IrBuilder *irb, Scope *scope, AstNode *node) {
     assert(node->type == NodeTypeErrorType);
-    return ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_pure_error);
+    return ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_pure_error, false);
 }
 
 static IrInstruction *ir_gen_defer(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
@@ -4922,7 +4957,7 @@ static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstN
     if (var_node) {
         assert(var_node->type == NodeTypeSymbol);
         IrInstruction *var_type = ir_build_const_type(irb, parent_scope, node,
-            irb->codegen->builtin_types.entry_pure_error);
+            irb->codegen->builtin_types.entry_pure_error, false);
         Buf *var_name = var_node->data.symbol_expr.symbol;
         bool is_const = true;
         bool is_shadowable = false;
@@ -5008,7 +5043,7 @@ static IrInstruction *ir_gen_container_decl(IrBuilder *irb, Scope *parent_scope,
     }
     irb->codegen->resolve_queue.append(&tld_container->base);
 
-    return ir_build_const_type(irb, parent_scope, node, container_type);
+    return ir_build_const_type(irb, parent_scope, node, container_type, false);
 }
 
 static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
@@ -6530,8 +6565,10 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc
             ConstExprValue *pointee = const_ptr_pointee(&ptr->value);
             if (pointee->special != ConstValSpecialRuntime) {
                 IrInstruction *result = ir_create_const(&ira->new_irb, source_instruction->scope,
-                    source_instruction->source_node, child_type, pointee->depends_on_compile_var);
+                    source_instruction->source_node, child_type, false);
                 result->value = *pointee;
+                result->value.depends_on_compile_var = pointee->depends_on_compile_var ||
+                    ptr->value.depends_on_compile_var;
                 return result;
             }
         }
@@ -6547,7 +6584,8 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc
         TypeTableEntry *ptr_type = ptr_val->data.x_type;
         if (ptr_type->id == TypeTableEntryIdPointer) {
             TypeTableEntry *child_type = ptr_type->data.pointer.child_type;
-            return ir_create_const_type(&ira->new_irb, source_instruction->scope, source_instruction->source_node, child_type);
+            return ir_create_const_type(&ira->new_irb, source_instruction->scope,
+                    source_instruction->source_node, child_type, ptr_val->depends_on_compile_var);
         } else {
             ir_add_error(ira, source_instruction,
                 buf_sprintf("attempt to dereference non pointer type '%s'", buf_ptr(&ptr_type->name)));
@@ -6572,7 +6610,7 @@ static TypeTableEntry *ir_analyze_ref(IrAnalyze *ira, IrInstruction *source_inst
         if (!val)
             return ira->codegen->builtin_types.entry_invalid;
         return ir_analyze_const_ptr(ira, source_instruction, val, value->value.type,
-                false, ConstPtrSpecialNone, is_const);
+                value->value.depends_on_compile_var, ConstPtrSpecialNone, is_const);
     }
 
     TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, value->value.type, true);
@@ -7367,6 +7405,7 @@ static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node
     Buf *param_name = param_decl_node->data.param_decl.name;
     VariableTableEntry *var = add_variable(ira->codegen, param_decl_node,
         *exec_scope, param_name, true, arg_val);
+    var->value.depends_on_compile_var = true;
     *exec_scope = var->child_scope;
     *next_proto_i += 1;
 
@@ -7408,6 +7447,7 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod
     Buf *param_name = param_decl_node->data.param_decl.name;
     VariableTableEntry *var = add_variable(ira->codegen, param_decl_node,
         *child_scope, param_name, true, arg_val);
+    var->value.depends_on_compile_var = true;
     *child_scope = var->child_scope;
 
     if (inline_arg || is_var_type) {
@@ -7986,6 +8026,14 @@ static TypeTableEntry *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstruct
         if (!ir_resolve_bool(ira, condition, &cond_is_true))
             return ir_unreach_error(ira);
 
+        if (!cond_br_instruction->base.is_gen && !condition->value.depends_on_compile_var &&
+                !ir_should_inline(&ira->new_irb))
+        {
+            const char *true_or_false = cond_is_true ? "true" : "false";
+            ir_add_error(ira, &cond_br_instruction->base,
+                buf_sprintf("condition is always %s; unnecessary if statement", true_or_false));
+        }
+
         IrBasicBlock *old_dest_block = cond_is_true ?
             cond_br_instruction->then_block : cond_br_instruction->else_block;
 
@@ -8028,9 +8076,9 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP
                 return ira->codegen->builtin_types.entry_invalid;
 
             if (value->value.special != ConstValSpecialRuntime) {
-                ConstExprValue *out_val = ir_build_const_from(ira, &phi_instruction->base,
-                        value->value.depends_on_compile_var);
+                ConstExprValue *out_val = ir_build_const_from(ira, &phi_instruction->base, true);
                 *out_val = value->value;
+                out_val->depends_on_compile_var = true;
             } else {
                 phi_instruction->base.other = value;
             }
@@ -8064,7 +8112,7 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP
     }
 
     if (new_incoming_blocks.length == 0) {
-        ir_build_const_from(ira, &phi_instruction->base, false);
+        ir_build_const_from(ira, &phi_instruction->base, true);
         return ira->codegen->builtin_types.entry_void;
     }
 
@@ -8080,7 +8128,9 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP
         return resolved_type;
 
     if (resolved_type->id == TypeTableEntryIdNumLitFloat ||
-        resolved_type->id == TypeTableEntryIdNumLitInt)
+        resolved_type->id == TypeTableEntryIdNumLitInt ||
+        resolved_type->id == TypeTableEntryIdNullLit ||
+        resolved_type->id == TypeTableEntryIdUndefLit)
     {
         ir_add_error_node(ira, phi_instruction->base.source_node,
                 buf_sprintf("unable to infer expression type"));
@@ -8109,7 +8159,7 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP
 }
 
 static TypeTableEntry *ir_analyze_var_ptr(IrAnalyze *ira, IrInstruction *instruction,
-        VariableTableEntry *var, bool is_const_ptr)
+        VariableTableEntry *var, bool is_const_ptr, bool depends_on_compile_var)
 {
     assert(var->value.type);
     if (var->value.type->id == TypeTableEntryIdInvalid)
@@ -8131,7 +8181,8 @@ static TypeTableEntry *ir_analyze_var_ptr(IrAnalyze *ira, IrInstruction *instruc
     if (mem_slot && mem_slot->special != ConstValSpecialRuntime) {
         ConstPtrSpecial ptr_special = is_comptime ? ConstPtrSpecialInline : ConstPtrSpecialNone;
         bool is_const = (var->value.type->id == TypeTableEntryIdMetaType) ? is_const_ptr : var->src_is_const;
-        return ir_analyze_const_ptr(ira, instruction, mem_slot, var->value.type, false, ptr_special, is_const);
+        return ir_analyze_const_ptr(ira, instruction, mem_slot, var->value.type,
+                mem_slot->depends_on_compile_var || depends_on_compile_var, ptr_special, is_const);
     } else {
         ir_build_var_ptr_from(&ira->new_irb, instruction, var, false);
         type_ensure_zero_bits_known(ira->codegen, var->value.type);
@@ -8141,7 +8192,7 @@ static TypeTableEntry *ir_analyze_var_ptr(IrAnalyze *ira, IrInstruction *instruc
 
 static TypeTableEntry *ir_analyze_instruction_var_ptr(IrAnalyze *ira, IrInstructionVarPtr *var_ptr_instruction) {
     VariableTableEntry *var = var_ptr_instruction->var;
-    return ir_analyze_var_ptr(ira, &var_ptr_instruction->base, var, var_ptr_instruction->is_const);
+    return ir_analyze_var_ptr(ira, &var_ptr_instruction->base, var, var_ptr_instruction->is_const, false);
 }
 
 static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstructionElemPtr *elem_ptr_instruction) {
@@ -8295,6 +8346,9 @@ static TypeTableEntry *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field
     ensure_complete_type(ira->codegen, bare_type);
 
     if (bare_type->id == TypeTableEntryIdStruct) {
+        if (bare_type->data.structure.is_invalid)
+            return ira->codegen->builtin_types.entry_invalid;
+
         TypeStructField *field = find_struct_type_field(bare_type, field_name);
         if (field) {
             ir_build_struct_field_ptr_from(&ira->new_irb, &field_ptr_instruction->base, container_ptr, field);
@@ -8304,6 +8358,9 @@ static TypeTableEntry *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field
                 field_ptr_instruction, container_ptr, container_type);
         }
     } else if (bare_type->id == TypeTableEntryIdEnum) {
+        if (bare_type->data.enumeration.is_invalid)
+            return ira->codegen->builtin_types.entry_invalid;
+
         TypeEnumField *field = find_enum_type_field(bare_type, field_name);
         if (field) {
             ir_build_enum_field_ptr_from(&ira->new_irb, &field_ptr_instruction->base, container_ptr, field);
@@ -8334,7 +8391,7 @@ static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source
         {
             TldVar *tld_var = (TldVar *)tld;
             VariableTableEntry *var = tld_var->var;
-            return ir_analyze_var_ptr(ira, source_instruction, var, false);
+            return ir_analyze_var_ptr(ira, source_instruction, var, false, depends_on_compile_var);
         }
         case TldIdFn:
         {
@@ -9065,9 +9122,8 @@ static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira,
         case TypeTableEntryIdEnumTag:
             {
                 uint64_t size_in_bytes = type_size(ira->codegen, type_entry);
-                bool depends_on_compile_var = false; // TODO types should be able to depend on compile var
                 ConstExprValue *out_val = ir_build_const_from(ira, &size_of_instruction->base,
-                        depends_on_compile_var);
+                        type_entry->size_depends_on_compile_var);
                 bignum_init_unsigned(&out_val->data.x_bignum, size_in_bytes);
                 return ira->codegen->builtin_types.entry_num_lit_int;
             }
@@ -9463,6 +9519,26 @@ static TypeTableEntry *ir_analyze_instruction_static_eval(IrAnalyze *ira,
     return value->value.type;
 }
 
+static TypeTableEntry *ir_analyze_instruction_generated_code(IrAnalyze *ira, IrInstructionGeneratedCode *instruction) {
+    IrInstruction *value = instruction->value->other;
+    if (value->value.type->id == TypeTableEntryIdInvalid)
+        return ira->codegen->builtin_types.entry_invalid;
+
+    if (instr_is_comptime(value)) {
+        ConstExprValue *val = ir_resolve_const(ira, value, UndefOk);
+        if (!val)
+            return ira->codegen->builtin_types.entry_invalid;
+
+        ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, false);
+        *out_val = *val;
+        out_val->depends_on_compile_var = true;
+        return value->value.type;
+    }
+
+    instruction->base.other = value;
+    return value->value.type;
+}
+
 static TypeTableEntry *ir_analyze_instruction_import(IrAnalyze *ira, IrInstructionImport *import_instruction) {
     IrInstruction *name_value = import_instruction->name->other;
     Buf *import_target_str = ir_resolve_str(ira, name_value);
@@ -11078,6 +11154,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
             return ir_analyze_instruction_enum_tag(ira, (IrInstructionEnumTag *)instruction);
         case IrInstructionIdStaticEval:
             return ir_analyze_instruction_static_eval(ira, (IrInstructionStaticEval *)instruction);
+        case IrInstructionIdGeneratedCode:
+            return ir_analyze_instruction_generated_code(ira, (IrInstructionGeneratedCode *)instruction);
         case IrInstructionIdImport:
             return ir_analyze_instruction_import(ira, (IrInstructionImport *)instruction);
         case IrInstructionIdArrayLen:
@@ -11284,6 +11362,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
         case IrInstructionIdSwitchTarget:
         case IrInstructionIdEnumTag:
         case IrInstructionIdStaticEval:
+        case IrInstructionIdGeneratedCode:
         case IrInstructionIdRef:
         case IrInstructionIdMinValue:
         case IrInstructionIdMaxValue:
src/ir_print.cpp
@@ -492,6 +492,12 @@ static void ir_print_static_eval(IrPrint *irp, IrInstructionStaticEval *instruct
     fprintf(irp->f, ")");
 }
 
+static void ir_print_generated_code(IrPrint *irp, IrInstructionGeneratedCode *instruction) {
+    fprintf(irp->f, "@generatedCode(");
+    ir_print_other_instruction(irp, instruction->value);
+    fprintf(irp->f, ")");
+}
+
 static void ir_print_import(IrPrint *irp, IrInstructionImport *instruction) {
     fprintf(irp->f, "@import(");
     ir_print_other_instruction(irp, instruction->name);
@@ -921,6 +927,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
         case IrInstructionIdStaticEval:
             ir_print_static_eval(irp, (IrInstructionStaticEval *)instruction);
             break;
+        case IrInstructionIdGeneratedCode:
+            ir_print_generated_code(irp, (IrInstructionGeneratedCode *)instruction);
+            break;
         case IrInstructionIdImport:
             ir_print_import(irp, (IrInstructionImport *)instruction);
             break;
test/cases/math.zig
@@ -72,9 +72,11 @@ fn modifyOperators() {
 
 fn threeExprInARow() {
     @setFnTest(this);
-
-    assertFalse(false || false || false);
-    assertFalse(true && true && false);
+    testThreeExprInARow(false, true);
+}
+fn testThreeExprInARow(f: bool, t: bool) {
+    assertFalse(f || f || f);
+    assertFalse(t && t && f);
     assertFalse(1 | 2 | 4 != 7);
     assertFalse(3 ^ 6 ^ 8 != 13);
     assertFalse(7 & 14 & 28 != 4);
@@ -86,6 +88,7 @@ fn threeExprInARow() {
     assertFalse(!!false);
     assertFalse(i32(7) != --(i32(7)));
 }
+
 fn assertFalse(b: bool) {
     assert(!b);
 }
test/cases/misc.zig
@@ -86,26 +86,29 @@ fn maxValueType() {
 
 fn shortCircuit() {
     @setFnTest(this);
+    testShortCircuit(false, true);
+}
 
-    var hit_1 = false;
-    var hit_2 = false;
-    var hit_3 = false;
-    var hit_4 = false;
+fn testShortCircuit(f: bool, t: bool) {
+    var hit_1 = f;
+    var hit_2 = f;
+    var hit_3 = f;
+    var hit_4 = f;
 
-    if (true || {assert(false); false}) {
-        hit_1 = true;
+    if (t || {assert(f); f}) {
+        hit_1 = t;
     }
-    if (false || { hit_2 = true; false }) {
-        assert(false);
+    if (f || { hit_2 = t; f }) {
+        assert(f);
     }
 
-    if (true && { hit_3 = true; false }) {
-        assert(false);
+    if (t && { hit_3 = t; f }) {
+        assert(f);
     }
-    if (false && {assert(false); false}) {
-        assert(false);
+    if (f && {assert(f); f}) {
+        assert(f);
     } else {
-        hit_4 = true;
+        hit_4 = t;
     }
     assert(hit_1);
     assert(hit_2);
test/cases/null.zig
@@ -3,7 +3,7 @@ const assert = @import("std").debug.assert;
 fn nullableType() {
     @setFnTest(this);
 
-    const x : ?bool = true;
+    const x : ?bool = @generatedCode(true);
 
     if (const y ?= x) {
         if (y) {
@@ -15,13 +15,13 @@ fn nullableType() {
         @unreachable();
     }
 
-    const next_x : ?i32 = null;
+    const next_x : ?i32 = @generatedCode(null);
 
     const z = next_x ?? 1234;
 
     assert(z == 1234);
 
-    const final_x : ?i32 = 13;
+    const final_x : ?i32 = @generatedCode(13);
 
     const num = final_x ?? @unreachable();
 
@@ -43,7 +43,7 @@ fn assignToIfVarPtr() {
 fn rhsMaybeUnwrapReturn() {
     @setFnTest(this);
 
-    const x: ?bool = true;
+    const x: ?bool = @generatedCode(true);
     const y = x ?? return;
 }