diff --git a/src/analyze.cpp b/src/analyze.cpp index 411a0d7a0..1ed4e1972 100644 --- a/src/analyze.cpp +++ b/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; diff --git a/src/analyze.hpp b/src/analyze.hpp index 7fa014350..6e8897bf8 100644 --- a/src/analyze.hpp +++ b/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); diff --git a/src/error.cpp b/src/error.cpp index 20d429e8b..753aeb292 100644 --- a/src/error.cpp +++ b/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)"; } diff --git a/src/ir.cpp b/src/ir.cpp index b6a73638e..67b5157c9 100644 --- a/src/ir.cpp +++ b/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(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; diff --git a/src/userland.h b/src/userland.h index 43bdbd18b..43356438f 100644 --- a/src/userland.h +++ b/src/userland.h @@ -75,6 +75,7 @@ enum Error { ErrorOperationAborted, ErrorBrokenPipe, ErrorNoSpaceLeft, + ErrorNotLazy, }; // ABI warning diff --git a/test/stage1/behavior/sizeof_and_typeof.zig b/test/stage1/behavior/sizeof_and_typeof.zig index cfad311e0..da79c3a27 100644 --- a/test/stage1/behavior/sizeof_and_typeof.zig +++ b/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); +}