From a1fb44f4d157601fb2183b7eca6582dbe66e7228 Mon Sep 17 00:00:00 2001 From: Karl Zylinski Date: Thu, 20 Aug 2026 11:45:50 +0200 Subject: [PATCH] Remove the `os.Error == 0` compatibility hack The hack made `0` legal against `os.Error` by blanking the constant's `ExactValue` via `update_untyped_expr_value`, which is a silent no-op when the expression has not been registered as an untyped expression yet. Since #7087, `check_expr_base` calls `convert_to_typed` before `add_untyped` runs, so the blanking is lost and the backend receives a constant typed `os.Error` still holding integer `0` with no variant type, tripping `GB_ASSERT_MSG(value_type != nullptr)` in `lb_const_value`. Also drop the redundant `operand->mode = Addressing_Invalid;` before the union `convert_untyped_error` call, which revives the `Did you want 'nil'?` hint, and guard that hint on `type_has_nil` and an Integer/Float zero. Co-Authored-By: Claude Opus 5 --- src/check_expr.cpp | 30 ++++++------------------------ 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 674b31340..b28a64ff0 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5060,8 +5060,12 @@ gb_internal void convert_untyped_error(CheckerContext *c, Operand *operand, Type gbString from_type_str = type_to_string(operand->type); char const *extra_text = ""; - if (operand->mode == Addressing_Constant) { - if (big_int_is_zero(&operand->value.value_integer)) { + if (operand->mode == Addressing_Constant && type_has_nil(target_type)) { + bool is_zero_int_or_float = + (operand->value.kind == ExactValue_Integer || operand->value.kind == ExactValue_Float) && + is_exact_value_zero(operand->value); + + if (is_zero_int_or_float) { if (make_string_c(expr_str) != "nil") { // HACK NOTE(bill): Just in case // NOTE(bill): Doesn't matter what the type is as it's still zero in the union extra_text = " - Did you want 'nil'?"; @@ -5275,27 +5279,6 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar case Type_Union: - // IMPORTANT NOTE HACK(bill): This is just to allow for comparisons against `0` with the `os.Error` type - // as a kind of transition period - if (!build_context.strict_style && - operand->mode == Addressing_Constant && - target_type->kind == Type_Named && - (c->pkg == nullptr || c->pkg->name != "os") && - target_type->Named.name == "Error") { - Entity *e = target_type->Named.type_name; - if (e->pkg && e->pkg->name == "os") { - if (is_exact_value_zero(operand->value) && - (operand->value.kind == ExactValue_Integer || - operand->value.kind == ExactValue_Float)) { - operand->mode = Addressing_Value; - // target_type = t_untyped_nil; - operand->value = empty_exact_value; - update_untyped_expr_value(c, operand->expr, operand->value); - break; - } - } - } - // "fallthrough" if (!is_operand_nil(*operand) && !is_operand_uninit(*operand)) { TEMPORARY_ALLOCATOR_GUARD(); @@ -5371,7 +5354,6 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar } else if (!is_type_untyped_nil(operand->type) || !type_has_nil(target_type)) { ERROR_BLOCK(); - operand->mode = Addressing_Invalid; convert_untyped_error(c, operand, target_type, true); if (count > 0) { error_line("'%s' is a union which only accepts the following types:\n", type_str);