diff --git a/src/analyze.cpp b/src/analyze.cpp index 235aeea682..7d51e83677 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1399,6 +1399,10 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) { enum_type->data.enumeration.is_invalid = true; add_node_error(g, decl_node->data.container_decl.init_arg_expr, buf_sprintf("expected integer, found '%s'", buf_ptr(&wanted_tag_int_type->name))); + } else if (wanted_tag_int_type->data.integral.is_signed) { + enum_type->data.enumeration.is_invalid = true; + add_node_error(g, decl_node->data.container_decl.init_arg_expr, + buf_sprintf("expected unsigned integer, found '%s'", buf_ptr(&wanted_tag_int_type->name))); } else if (wanted_tag_int_type->data.integral.bit_count < tag_int_type->data.integral.bit_count) { enum_type->data.enumeration.is_invalid = true; add_node_error(g, decl_node->data.container_decl.init_arg_expr, diff --git a/src/ir.cpp b/src/ir.cpp index c8617e3a8a..c6003fbf32 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -8911,7 +8911,17 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst wanted_type->id == TypeTableEntryIdEnum && wanted_type->data.enumeration.gen_field_count == 0) { - return ir_analyze_int_to_enum(ira, source_instr, value, wanted_type); + ensure_complete_type(ira->codegen, wanted_type); + if (type_is_invalid(wanted_type)) + return ira->codegen->invalid_instruction; + if (actual_type == wanted_type->data.enumeration.tag_type->data.enum_tag.int_type) { + return ir_analyze_int_to_enum(ira, source_instr, value, wanted_type); + } + ir_add_error(ira, source_instr, + buf_sprintf("integer to enum cast from '%s' instead of its tag type, '%s'", + buf_ptr(&actual_type->name), + buf_ptr(&wanted_type->data.enumeration.tag_type->data.enum_tag.int_type->name))); + return ira->codegen->invalid_instruction; } // explicit cast from enum type with no payload to integer @@ -8919,7 +8929,17 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst actual_type->id == TypeTableEntryIdEnum && actual_type->data.enumeration.gen_field_count == 0) { - return ir_analyze_enum_to_int(ira, source_instr, value, wanted_type); + ensure_complete_type(ira->codegen, actual_type); + if (type_is_invalid(actual_type)) + return ira->codegen->invalid_instruction; + if (wanted_type == actual_type->data.enumeration.tag_type->data.enum_tag.int_type) { + return ir_analyze_enum_to_int(ira, source_instr, value, wanted_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_type->data.enum_tag.int_type->name))); + return ira->codegen->invalid_instruction; } // explicit cast from undefined to anything diff --git a/test/cases/enum.zig b/test/cases/enum.zig index 1b2ff10a9b..6df858a48f 100644 --- a/test/cases/enum.zig +++ b/test/cases/enum.zig @@ -89,8 +89,8 @@ test "enum to int" { shouldEqual(Number.Four, 4); } -fn shouldEqual(n: Number, expected: usize) { - assert(usize(n) == expected); +fn shouldEqual(n: Number, expected: u3) { + assert(u3(n) == expected); } @@ -98,7 +98,7 @@ test "int to enum" { testIntToEnumEval(3); } fn testIntToEnumEval(x: i32) { - assert(IntToEnumNumber(x) == IntToEnumNumber.Three); + assert(IntToEnumNumber(u3(x)) == IntToEnumNumber.Three); } const IntToEnumNumber = enum { Zero, @@ -284,3 +284,11 @@ fn getC(data: &const BitFieldOfEnums) -> C { return data.c; } +test "casting enum to its tag type" { + testCastEnumToTagType(Small2.Two); + comptime testCastEnumToTagType(Small2.Two); +} + +fn testCastEnumToTagType(value: Small2) { + assert(u2(value) == 1); +} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 9b5ec6a6d6..367dec08b3 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2390,4 +2390,61 @@ pub fn addCases(cases: &tests.CompileErrorContext) { \\} , ".tmp_source.zig:1:20: error: expected integer, found 'f32'"); + + cases.add("implicitly casting enum to tag type", + \\const Small = enum(u2) { + \\ One, + \\ Two, + \\ Three, + \\ Four, + \\}; + \\ + \\export fn entry() { + \\ var x: u2 = Small.Two; + \\} + , + ".tmp_source.zig:9:22: error: expected type 'u2', found 'Small'"); + + cases.add("explicitly casting enum to non tag type", + \\const Small = enum(u2) { + \\ One, + \\ Two, + \\ Three, + \\ Four, + \\}; + \\ + \\export fn entry() { + \\ var x = u3(Small.Two); + \\} + , + ".tmp_source.zig:9:15: error: enum to integer cast to 'u3' instead of its tag type, 'u2'"); + + cases.add("explicitly casting non tag type to enum", + \\const Small = enum(u2) { + \\ One, + \\ Two, + \\ Three, + \\ Four, + \\}; + \\ + \\export fn entry() { + \\ var y = u3(3); + \\ var x = Small(y); + \\} + , + ".tmp_source.zig:10:18: error: integer to enum cast from 'u3' instead of its tag type, 'u2'"); + + cases.add("non unsigned integer enum tag type", + \\const Small = enum(i2) { + \\ One, + \\ Two, + \\ Three, + \\ Four, + \\}; + \\ + \\export fn entry() { + \\ var y = Small.Two; + \\} + , + ".tmp_source.zig:1:19: error: expected unsigned integer, found 'i2'"); }