Commit 0512beca9d

Andrew Kelley <andrew@ziglang.org>
2019-08-29 20:46:22
comparing against zero participates in lazy values
1 parent d9f0446
src/analyze.cpp
@@ -973,7 +973,7 @@ ConstExprValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, Zig
             nullptr, nullptr, node, type_name, nullptr, nullptr, undef);
 }
 
-static Error type_val_resolve_zero_bits(CodeGen *g, ConstExprValue *type_val, ZigType *parent_type,
+Error type_val_resolve_zero_bits(CodeGen *g, ConstExprValue *type_val, ZigType *parent_type,
         ConstExprValue *parent_type_val, bool *is_zero_bits)
 {
     Error err;
src/analyze.hpp
@@ -249,6 +249,8 @@ bool fn_is_async(ZigFn *fn);
 Error type_val_resolve_abi_align(CodeGen *g, ConstExprValue *type_val, uint32_t *abi_align);
 Error type_val_resolve_abi_size(CodeGen *g, AstNode *source_node, ConstExprValue *type_val,
         size_t *abi_size, size_t *size_in_bits);
+Error type_val_resolve_zero_bits(CodeGen *g, ConstExprValue *type_val, ZigType *parent_type,
+        ConstExprValue *parent_type_val, bool *is_zero_bits);
 ZigType *resolve_union_field_type(CodeGen *g, TypeUnionField *union_field);
 ZigType *resolve_struct_field_type(CodeGen *g, TypeStructField *struct_field);
 
src/error.cpp
@@ -55,6 +55,7 @@ const char *err_str(Error err) {
         case ErrorBrokenPipe: return "broken pipe";
         case ErrorNoSpaceLeft: return "no space left";
         case ErrorNoCCompilerInstalled: return "no C compiler installed";
+        case ErrorNotLazy: return "not lazy";
     }
     return "(invalid error)";
 }
src/ir.cpp
@@ -12932,7 +12932,52 @@ static bool optional_value_is_null(ConstExprValue *val) {
     }
 }
 
+// Returns ErrorNotLazy when the value cannot be determined
+static Error lazy_cmp_zero(AstNode *source_node, ConstExprValue *val, Cmp *result) {
+    Error err;
+
+    switch (val->special) {
+        case ConstValSpecialRuntime:
+        case ConstValSpecialUndef:
+            return ErrorNotLazy;
+        case ConstValSpecialStatic:
+            switch (val->type->id) {
+                case ZigTypeIdComptimeInt:
+                case ZigTypeIdInt:
+                    *result = bigint_cmp_zero(&val->data.x_bigint);
+                    return ErrorNone;
+                default:
+                    return ErrorNotLazy;
+            }
+        case ConstValSpecialLazy:
+            switch (val->data.x_lazy->id) {
+                case LazyValueIdInvalid:
+                    zig_unreachable();
+                case LazyValueIdAlignOf:
+                    *result = CmpGT;
+                    return ErrorNone;
+                case LazyValueIdSizeOf: {
+                    LazyValueSizeOf *lazy_size_of = reinterpret_cast<LazyValueSizeOf *>(val->data.x_lazy);
+                    IrAnalyze *ira = lazy_size_of->ira;
+                    bool is_zero_bits;
+                    if ((err = type_val_resolve_zero_bits(ira->codegen, &lazy_size_of->target_type->value,
+                        nullptr, nullptr, &is_zero_bits)))
+                    {
+                        return err;
+                    }
+                    *result = is_zero_bits ? CmpEQ : CmpGT;
+                    return ErrorNone;
+                }
+                default:
+                    return ErrorNotLazy;
+            }
+    }
+    zig_unreachable();
+}
+
 static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
+    Error err;
+
     IrInstruction *op1 = bin_op_instruction->op1->child;
     if (type_is_invalid(op1->value.type))
         return ira->codegen->invalid_instruction;
@@ -13182,6 +13227,50 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *
     }
 
     if (one_possible_value || (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2))) {
+        {
+            // Before resolving the values, we special case comparisons against zero. These can often be done
+            // without resolving lazy values, preventing potential dependency loops.
+            Cmp op1_cmp_zero;
+            if ((err = lazy_cmp_zero(bin_op_instruction->base.source_node, &casted_op1->value, &op1_cmp_zero))) {
+                if (err == ErrorNotLazy) goto never_mind_just_calculate_it_normally;
+                return ira->codegen->invalid_instruction;
+            }
+            Cmp op2_cmp_zero;
+            if ((err = lazy_cmp_zero(bin_op_instruction->base.source_node, &casted_op2->value, &op2_cmp_zero))) {
+                if (err == ErrorNotLazy) goto never_mind_just_calculate_it_normally;
+                return ira->codegen->invalid_instruction;
+            }
+            bool can_cmp_zero = false;
+            Cmp cmp_result;
+            if (op1_cmp_zero == CmpEQ && op2_cmp_zero == CmpEQ) {
+                can_cmp_zero = true;
+                cmp_result = CmpEQ;
+            } else if (op1_cmp_zero == CmpGT && op2_cmp_zero == CmpEQ) {
+                can_cmp_zero = true;
+                cmp_result = CmpGT;
+            } else if (op1_cmp_zero == CmpEQ && op2_cmp_zero == CmpGT) {
+                can_cmp_zero = true;
+                cmp_result = CmpLT;
+            } else if (op1_cmp_zero == CmpLT && op2_cmp_zero == CmpEQ) {
+                can_cmp_zero = true;
+                cmp_result = CmpLT;
+            } else if (op1_cmp_zero == CmpEQ && op2_cmp_zero == CmpLT) {
+                can_cmp_zero = true;
+                cmp_result = CmpGT;
+            } else if (op1_cmp_zero == CmpLT && op2_cmp_zero == CmpGT) {
+                can_cmp_zero = true;
+                cmp_result = CmpLT;
+            } else if (op1_cmp_zero == CmpGT && op2_cmp_zero == CmpLT) {
+                can_cmp_zero = true;
+                cmp_result = CmpGT;
+            }
+            if (can_cmp_zero) {
+                bool answer = resolve_cmp_op_id(op_id, cmp_result);
+                return ir_const_bool(ira, &bin_op_instruction->base, answer);
+            }
+        }
+never_mind_just_calculate_it_normally:
+
         ConstExprValue *op1_val = one_possible_value ? &casted_op1->value : ir_resolve_const(ira, casted_op1, UndefBad);
         if (op1_val == nullptr)
             return ira->codegen->invalid_instruction;
src/userland.h
@@ -75,6 +75,7 @@ enum Error {
     ErrorOperationAborted,
     ErrorBrokenPipe,
     ErrorNoSpaceLeft,
+    ErrorNotLazy,
 };
 
 // ABI warning
test/stage1/behavior/sizeof_and_typeof.zig
@@ -74,3 +74,18 @@ test "@sizeOf on compile-time types" {
     expect(@sizeOf(@typeOf(.hi)) == 0);
     expect(@sizeOf(@typeOf(type)) == 0);
 }
+
+test "@sizeOf(T) == 0 doesn't force resolving struct size" {
+    const S = struct {
+        const Foo = struct {
+            y: if (@sizeOf(Foo) == 0) u64 else u32,
+        };
+        const Bar = struct {
+            x: i32,
+            y: if (0 == @sizeOf(Bar)) u64 else u32,
+        };
+    };
+
+    expect(@sizeOf(S.Foo) == 4);
+    expect(@sizeOf(S.Bar) == 8);
+}