diff --git a/doc/langref.html.in b/doc/langref.html.in index 1341bf1be..ac3864add 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -2221,8 +2221,9 @@ test "packed enum" { {#header_close#} {#header_open|union#} {#code_begin|test|union#} -const assert = @import("std").debug.assert; -const mem = @import("std").mem; +const std = @import("std"); +const assert = std.debug.assert; +const mem = std.mem; // A union has only 1 active field at a time. const Payload = union { @@ -2231,16 +2232,19 @@ const Payload = union { Bool: bool, }; test "simple union" { - var payload = Payload {.Int = 1234}; + var payload = Payload{ .Int = 1234 }; // payload.Float = 12.34; // ERROR! field not active assert(payload.Int == 1234); // You can activate another field by assigning the entire union. - payload = Payload {.Float = 12.34}; + payload = Payload{ .Float = 12.34 }; assert(payload.Float == 12.34); } // Unions can be given an enum tag type: -const ComplexTypeTag = enum { Ok, NotOk }; +const ComplexTypeTag = enum { + Ok, + NotOk, +}; const ComplexType = union(ComplexTypeTag) { Ok: u8, NotOk: void, @@ -2248,11 +2252,11 @@ const ComplexType = union(ComplexTypeTag) { // Declare a specific instance of the union variant. test "declare union value" { - const c = ComplexType { .Ok = 0 }; + const c = ComplexType{ .Ok = 0 }; assert(ComplexTypeTag(c) == ComplexTypeTag.Ok); } -// @TagType can be used to access the enum tag type of a union. +// @TagType can be used to access the enum tag type of a tagged union. test "@TagType" { assert(@TagType(ComplexType) == ComplexTypeTag); } @@ -2266,7 +2270,7 @@ const Foo = union(enum) { None, }; test "union variant switch" { - const p = Foo { .Number = 54 }; + const p = Foo{ .Number = 54 }; const what_is_it = switch (p) { // Capture by reference Foo.String => |*x| blk: { @@ -2301,14 +2305,13 @@ const Variant = union(enum) { }; test "union method" { - var v1 = Variant { .Int = 1 }; - var v2 = Variant { .Bool = false }; + var v1 = Variant{ .Int = 1 }; + var v2 = Variant{ .Bool = false }; assert(v1.truthy()); assert(!v2.truthy()); } - const Small = union { A: i32, B: bool, @@ -5660,12 +5663,13 @@ test "main" { {#header_close#} {#header_open|@enumToInt#} -
{#syntax#}@enumToInt(enum_value: var) var{#endsyntax#}
+
{#syntax#}@enumToInt(enum_or_tagged_union: var) var{#endsyntax#}

- Converts an enumeration value into its integer tag type. + Converts an enumeration value into its integer tag type. When a tagged union is passed, + the tag value is used as the enumeration value.

- If the enum has only 1 possible value, the resut is a {#syntax#}comptime_int{#endsyntax#} + If there is only one possible enum value, the resut is a {#syntax#}comptime_int{#endsyntax#} known at {#link|comptime#}.

{#see_also|@intToEnum#} diff --git a/src/ir.cpp b/src/ir.cpp index 063be4e95..d51df5237 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -10305,49 +10305,75 @@ static IrInstruction *ir_analyze_array_to_slice(IrAnalyze *ira, IrInstruction *s return result; } -static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *source_instr, - IrInstruction *target, ZigType *wanted_type) -{ +static ZigType *ir_resolve_union_tag_type(IrAnalyze *ira, IrInstruction *source_instr, ZigType *union_type) { + assert(union_type->id == ZigTypeIdUnion); + Error err; - assert(wanted_type->id == ZigTypeIdInt || wanted_type->id == ZigTypeIdComptimeInt); + if ((err = type_resolve(ira->codegen, union_type, ResolveStatusSizeKnown))) + return ira->codegen->builtin_types.entry_invalid; - ZigType *actual_type = target->value.type; - if ((err = ensure_complete_type(ira->codegen, actual_type))) - return ira->codegen->invalid_instruction; + AstNode *decl_node = union_type->data.unionation.decl_node; + if (decl_node->data.container_decl.auto_enum || decl_node->data.container_decl.init_arg_expr != nullptr) { + assert(union_type->data.unionation.tag_type != nullptr); + return union_type->data.unionation.tag_type; + } else { + ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("union '%s' has no tag", + buf_ptr(&union_type->name))); + add_error_note(ira->codegen, msg, decl_node, buf_sprintf("consider 'union(enum)' here")); + return ira->codegen->builtin_types.entry_invalid; + } +} - if (wanted_type != actual_type->data.enumeration.tag_int_type) { - ir_add_error(ira, source_instr, - buf_sprintf("enum to integer cast to '%s' instead of its tag type, '%s'", - buf_ptr(&wanted_type->name), - buf_ptr(&actual_type->data.enumeration.tag_int_type->name))); +static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target) { + Error err; + + IrInstruction *enum_target; + ZigType *enum_type; + if (target->value.type->id == ZigTypeIdUnion) { + enum_type = ir_resolve_union_tag_type(ira, target, target->value.type); + if (type_is_invalid(enum_type)) + return ira->codegen->invalid_instruction; + enum_target = ir_implicit_cast(ira, target, enum_type); + if (type_is_invalid(enum_target->value.type)) + return ira->codegen->invalid_instruction; + } else if (target->value.type->id == ZigTypeIdEnum) { + enum_target = target; + enum_type = target->value.type; + } else { + ir_add_error(ira, target, + buf_sprintf("expected enum, found type '%s'", buf_ptr(&target->value.type->name))); return ira->codegen->invalid_instruction; } - assert(actual_type->id == ZigTypeIdEnum); + if ((err = type_resolve(ira->codegen, enum_type, ResolveStatusSizeKnown))) + return ira->codegen->invalid_instruction; - if (instr_is_comptime(target)) { - ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); + ZigType *tag_type = enum_type->data.enumeration.tag_int_type; + assert(tag_type->id == ZigTypeIdInt || tag_type->id == ZigTypeIdComptimeInt); + + if (instr_is_comptime(enum_target)) { + ConstExprValue *val = ir_resolve_const(ira, enum_target, UndefBad); if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_const(ira, source_instr, wanted_type); - init_const_bigint(&result->value, wanted_type, &val->data.x_enum_tag); + IrInstruction *result = ir_const(ira, source_instr, tag_type); + init_const_bigint(&result->value, tag_type, &val->data.x_enum_tag); return result; } // If there is only one possible tag, then we know at comptime what it is. - if (actual_type->data.enumeration.layout == ContainerLayoutAuto && - actual_type->data.enumeration.src_field_count == 1) + if (enum_type->data.enumeration.layout == ContainerLayoutAuto && + enum_type->data.enumeration.src_field_count == 1) { - assert(wanted_type== ira->codegen->builtin_types.entry_num_lit_int); - IrInstruction *result = ir_const(ira, source_instr, wanted_type); - init_const_bigint(&result->value, wanted_type, - &actual_type->data.enumeration.fields[0].value); + assert(tag_type == ira->codegen->builtin_types.entry_num_lit_int); + IrInstruction *result = ir_const(ira, source_instr, tag_type); + init_const_bigint(&result->value, tag_type, + &enum_type->data.enumeration.fields[0].value); return result; } IrInstruction *result = ir_build_widen_or_shorten(&ira->new_irb, source_instr->scope, - source_instr->source_node, target); - result->value.type = wanted_type; + source_instr->source_node, enum_target); + result->value.type = tag_type; return result; } @@ -14358,12 +14384,12 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source if (dst_size == 0) return ErrorNone; opt_ir_add_error_node(ira, codegen, source_node, - buf_sprintf("attempt to read %zu bytes from null pointer", + buf_sprintf("attempt to read %" ZIG_PRI_usize " bytes from null pointer", dst_size)); return ErrorSemanticAnalyzeFail; case ConstPtrSpecialRef: { opt_ir_add_error_node(ira, codegen, source_node, - buf_sprintf("attempt to read %zu bytes from pointer to %s which is %zu bytes", + buf_sprintf("attempt to read %" ZIG_PRI_usize " bytes from pointer to %s which is %" ZIG_PRI_usize " bytes", dst_size, buf_ptr(&pointee->type->name), src_size)); return ErrorSemanticAnalyzeFail; } @@ -14377,7 +14403,7 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source src_size = elem_size * (array_val->type->data.array.len - elem_index); if (dst_size > src_size) { opt_ir_add_error_node(ira, codegen, source_node, - buf_sprintf("attempt to read %zu bytes from %s at index %" ZIG_PRI_usize " which is %zu bytes", + buf_sprintf("attempt to read %" ZIG_PRI_usize " bytes from %s at index %" ZIG_PRI_usize " which is %" ZIG_PRI_usize " bytes", dst_size, buf_ptr(&array_val->type->name), elem_index, src_size)); return ErrorSemanticAnalyzeFail; } @@ -21364,20 +21390,10 @@ static IrInstruction *ir_analyze_instruction_tag_type(IrAnalyze *ira, IrInstruct return ir_const_type(ira, &instruction->base, enum_type->data.enumeration.tag_int_type); } else if (enum_type->id == ZigTypeIdUnion) { - if ((err = ensure_complete_type(ira->codegen, enum_type))) + ZigType *tag_type = ir_resolve_union_tag_type(ira, instruction->target, enum_type); + if (type_is_invalid(tag_type)) return ira->codegen->invalid_instruction; - - AstNode *decl_node = enum_type->data.unionation.decl_node; - if (decl_node->data.container_decl.auto_enum || decl_node->data.container_decl.init_arg_expr != nullptr) { - assert(enum_type->data.unionation.tag_type != nullptr); - - return ir_const_type(ira, &instruction->base, enum_type->data.unionation.tag_type); - } else { - ErrorMsg *msg = ir_add_error(ira, target_inst, buf_sprintf("union '%s' has no tag", - buf_ptr(&enum_type->name))); - add_error_note(ira->codegen, msg, decl_node, buf_sprintf("consider 'union(enum)' here")); - return ira->codegen->invalid_instruction; - } + return ir_const_type(ira, &instruction->base, tag_type); } else { ir_add_error(ira, target_inst, buf_sprintf("expected enum or union, found '%s'", buf_ptr(&enum_type->name))); @@ -21958,23 +21974,11 @@ static IrInstruction *ir_analyze_instruction_bit_reverse(IrAnalyze *ira, IrInstr static IrInstruction *ir_analyze_instruction_enum_to_int(IrAnalyze *ira, IrInstructionEnumToInt *instruction) { - Error err; IrInstruction *target = instruction->target->child; if (type_is_invalid(target->value.type)) return ira->codegen->invalid_instruction; - if (target->value.type->id != ZigTypeIdEnum) { - ir_add_error(ira, instruction->target, - buf_sprintf("expected enum, found type '%s'", buf_ptr(&target->value.type->name))); - return ira->codegen->invalid_instruction; - } - - if ((err = type_resolve(ira->codegen, target->value.type, ResolveStatusZeroBitsKnown))) - return ira->codegen->invalid_instruction; - - ZigType *tag_type = target->value.type->data.enumeration.tag_int_type; - - return ir_analyze_enum_to_int(ira, &instruction->base, target, tag_type); + return ir_analyze_enum_to_int(ira, &instruction->base, target); } static IrInstruction *ir_analyze_instruction_int_to_enum(IrAnalyze *ira, IrInstructionIntToEnum *instruction) { diff --git a/src/target.cpp b/src/target.cpp index cea782631..5352a2182 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -9,6 +9,7 @@ #include "error.hpp" #include "target.hpp" #include "util.hpp" +#include "os.hpp" #include @@ -848,7 +849,7 @@ const char *target_lib_file_ext(ZigTarget *target, bool is_static, size_t versio if (is_static) { return ".a"; } else { - return buf_ptr(buf_sprintf(".so.%zu", version_major)); + return buf_ptr(buf_sprintf(".so.%" ZIG_PRI_usize, version_major)); } } } diff --git a/test/stage1/behavior/union.zig b/test/stage1/behavior/union.zig index 0a4e2cfb9..bbba86766 100644 --- a/test/stage1/behavior/union.zig +++ b/test/stage1/behavior/union.zig @@ -350,3 +350,18 @@ test "union with only 1 field casted to its enum type which has enum value speci expect(@enumToInt(t) == 33); comptime expect(@enumToInt(t) == 33); } + +test "@enumToInt works on unions" { + const Bar = union(enum) { + A: bool, + B: u8, + C, + }; + + const a = Bar{ .A = true }; + var b = Bar{ .B = undefined }; + var c = Bar.C; + expect(@enumToInt(a) == 0); + expect(@enumToInt(b) == 1); + expect(@enumToInt(c) == 2); +} diff --git a/test/tests.zig b/test/tests.zig index 656c05cb9..f9c9207f0 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -572,9 +572,7 @@ pub const CompileErrorContext = struct { const source_file = ".tmp_source.zig"; fn init(input: []const u8) ErrLineIter { - return ErrLineIter { - .lines = mem.separate(input, "\n"), - }; + return ErrLineIter{ .lines = mem.separate(input, "\n") }; } fn next(self: *ErrLineIter) ?[]const u8 { @@ -718,11 +716,10 @@ pub const CompileErrorContext = struct { for (self.case.expected_errors.toSliceConst()) |expected| { if (mem.indexOf(u8, stderr, expected) == null) { warn( - \\=========== Expected compile error: ============ + \\\n=========== Expected compile error: ============ \\{} \\ - , expected - ); + , expected); ok = false; break; } @@ -734,8 +731,7 @@ pub const CompileErrorContext = struct { \\================= Full output: ================= \\{} \\ - , stderr - ); + , stderr); return error.TestFailed; }