From bb38931c7146678548f63ae9ef71e534c7598fe3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 23 Aug 2021 15:30:57 -0700 Subject: [PATCH] stage1: `@intToEnum` implicitly does an `@intCast` This is a backwards-compatible language change. Previously, `@intToEnum` coerced its integer operand to the integer tag type of the destination enum type, often requiring the callsite to additionally wrap the operand in an `@intCast`. Now, the `@intCast` is implicit, and any integer operand can be passed to `@intToEnum`. The same as before, it is illegal behavior to pass any integer which does not have a corresponding enum tag. --- doc/langref.html.in | 6 ++++-- src/stage1/ir.cpp | 39 +++++++++++++++++++++++++-------------- test/behavior/enum.zig | 2 +- test/compile_errors.zig | 4 ++-- 4 files changed, 32 insertions(+), 19 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 3691cbba28..336db082f1 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -3163,7 +3163,9 @@ test "switch using enum literals" { It must specify a tag type and cannot consume every enumeration value.

- {#link|@intToEnum#} on a non-exhaustive enum cannot fail. + {#link|@intToEnum#} on a non-exhaustive enum involves the safety semantics + of {#link|@intCast#} to the integer tag type, but beyond that always results in + a well-defined enum value.

A switch on a non-exhaustive enum can include a '_' prong as an alternative to an {#syntax#}else{#endsyntax#} prong @@ -7972,7 +7974,7 @@ test "@hasDecl" { {#header_close#} {#header_open|@intToEnum#} -

{#syntax#}@intToEnum(comptime DestType: type, int_value: std.meta.Tag(DestType)) DestType{#endsyntax#}
+
{#syntax#}@intToEnum(comptime DestType: type, integer: anytype) DestType{#endsyntax#}

Converts an integer into an {#link|enum#} value.

diff --git a/src/stage1/ir.cpp b/src/stage1/ir.cpp index b1583cc6b4..830ce76708 100644 --- a/src/stage1/ir.cpp +++ b/src/stage1/ir.cpp @@ -20007,29 +20007,24 @@ static Stage1AirInst *ir_analyze_instruction_truncate(IrAnalyze *ira, Stage1ZirI return ir_build_truncate_gen(ira, instruction->base.scope, instruction->base.source_node, dest_type, target); } -static Stage1AirInst *ir_analyze_instruction_int_cast(IrAnalyze *ira, Stage1ZirInstIntCast *instruction) { - ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); - if (type_is_invalid(dest_type)) - return ira->codegen->invalid_inst_gen; - +static Stage1AirInst *ir_analyze_int_cast(IrAnalyze *ira, Scope *scope, AstNode *source_node, + ZigType *dest_type, AstNode *dest_type_src_node, + Stage1AirInst *target, AstNode *target_src_node) +{ ZigType *scalar_dest_type = (dest_type->id == ZigTypeIdVector) ? dest_type->data.vector.elem_type : dest_type; if (scalar_dest_type->id != ZigTypeIdInt && scalar_dest_type->id != ZigTypeIdComptimeInt) { - ir_add_error_node(ira, instruction->dest_type->source_node, + ir_add_error_node(ira, dest_type_src_node, buf_sprintf("expected integer type, found '%s'", buf_ptr(&scalar_dest_type->name))); return ira->codegen->invalid_inst_gen; } - Stage1AirInst *target = instruction->target->child; - if (type_is_invalid(target->value->type)) - return ira->codegen->invalid_inst_gen; - ZigType *scalar_target_type = (target->value->type->id == ZigTypeIdVector) ? target->value->type->data.vector.elem_type : target->value->type; if (scalar_target_type->id != ZigTypeIdInt && scalar_target_type->id != ZigTypeIdComptimeInt) { - ir_add_error_node(ira, instruction->target->source_node, buf_sprintf("expected integer type, found '%s'", + ir_add_error_node(ira, target_src_node, buf_sprintf("expected integer type, found '%s'", buf_ptr(&scalar_target_type->name))); return ira->codegen->invalid_inst_gen; } @@ -20039,10 +20034,24 @@ static Stage1AirInst *ir_analyze_instruction_int_cast(IrAnalyze *ira, Stage1ZirI if (val == nullptr) return ira->codegen->invalid_inst_gen; - return ir_implicit_cast2(ira, instruction->target->scope, instruction->target->source_node, target, dest_type); + return ir_implicit_cast2(ira, scope, target_src_node, target, dest_type); } - return ir_analyze_widen_or_shorten(ira, instruction->base.scope, instruction->base.source_node, target, dest_type); + return ir_analyze_widen_or_shorten(ira, scope, source_node, target, dest_type); +} + +static Stage1AirInst *ir_analyze_instruction_int_cast(IrAnalyze *ira, Stage1ZirInstIntCast *instruction) { + ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); + if (type_is_invalid(dest_type)) + return ira->codegen->invalid_inst_gen; + + Stage1AirInst *target = instruction->target->child; + if (type_is_invalid(target->value->type)) + return ira->codegen->invalid_inst_gen; + + return ir_analyze_int_cast(ira, instruction->base.scope, instruction->base.source_node, + dest_type, instruction->dest_type->source_node, + target, instruction->target->source_node); } static Stage1AirInst *ir_analyze_instruction_float_cast(IrAnalyze *ira, Stage1ZirInstFloatCast *instruction) { @@ -24282,7 +24291,9 @@ static Stage1AirInst *ir_analyze_instruction_int_to_enum(IrAnalyze *ira, Stage1Z if (type_is_invalid(target->value->type)) return ira->codegen->invalid_inst_gen; - Stage1AirInst *casted_target = ir_implicit_cast(ira, target, tag_type); + Stage1AirInst *casted_target = ir_analyze_int_cast(ira, instruction->base.scope, + instruction->base.source_node, tag_type, instruction->dest_type->source_node, + target, instruction->target->source_node); if (type_is_invalid(casted_target->value->type)) return ira->codegen->invalid_inst_gen; diff --git a/test/behavior/enum.zig b/test/behavior/enum.zig index dbda9f3238..fd526e1bef 100644 --- a/test/behavior/enum.zig +++ b/test/behavior/enum.zig @@ -203,7 +203,7 @@ test "int to enum" { try testIntToEnumEval(3); } fn testIntToEnumEval(x: i32) !void { - try expect(@intToEnum(IntToEnumNumber, @intCast(u3, x)) == IntToEnumNumber.Three); + try expect(@intToEnum(IntToEnumNumber, x) == IntToEnumNumber.Three); } const IntToEnumNumber = enum { Zero, diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 6075eee33a..14d2a98e02 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -7691,12 +7691,12 @@ pub fn addCases(ctx: *TestContext) !void { \\}; \\ \\export fn entry() void { - \\ var y = @as(u3, 3); + \\ var y = @as(f32, 3); \\ var x = @intToEnum(Small, y); \\ _ = x; \\} , &[_][]const u8{ - "tmp.zig:10:31: error: expected type 'u2', found 'u3'", + "tmp.zig:10:31: error: expected integer type, found 'f32'", }); ctx.objErrStage1("union fields with value assignments",