diff --git a/src/all_types.hpp b/src/all_types.hpp index 510905a2a..6f4947d43 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -429,14 +429,12 @@ enum CastOp { CastOpNoop, // fn call expr is a cast, but does nothing CastOpPtrToInt, CastOpIntToPtr, - CastOpWidenOrShorten, CastOpErrToInt, CastOpIntToFloat, CastOpFloatToInt, CastOpBoolToInt, CastOpResizeSlice, CastOpIntToEnum, - CastOpEnumToInt, CastOpBytesToSlice, }; @@ -1454,6 +1452,7 @@ enum IrInstructionId { IrInstructionIdTestComptime, IrInstructionIdInitEnum, IrInstructionIdPointerReinterpret, + IrInstructionIdWidenOrShorten, }; struct IrInstruction { @@ -2096,6 +2095,12 @@ struct IrInstructionPointerReinterpret { IrInstruction *ptr; }; +struct IrInstructionWidenOrShorten { + IrInstruction base; + + IrInstruction *target; +}; + enum LValPurpose { LValPurposeNone, LValPurposeAssign, diff --git a/src/codegen.cpp b/src/codegen.cpp index 123caea75..39cd6c8fd 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -973,9 +973,6 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, ""); case CastOpIntToPtr: return LLVMBuildIntToPtr(g->builder, expr_val, wanted_type->type_ref, ""); - case CastOpWidenOrShorten: - return gen_widen_or_shorten(g, ir_want_debug_safety(g, &cast_instruction->base), - actual_type, wanted_type, expr_val); case CastOpResizeSlice: { assert(cast_instruction->tmp_ptr); @@ -1086,9 +1083,6 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, case CastOpIntToEnum: return gen_widen_or_shorten(g, ir_want_debug_safety(g, &cast_instruction->base), actual_type, wanted_type->data.enumeration.tag_type, expr_val); - case CastOpEnumToInt: - return gen_widen_or_shorten(g, ir_want_debug_safety(g, &cast_instruction->base), - actual_type->data.enumeration.tag_type, wanted_type, expr_val); } zig_unreachable(); } @@ -1101,6 +1095,24 @@ static LLVMValueRef ir_render_pointer_reinterpret(CodeGen *g, IrExecutable *exec return LLVMBuildBitCast(g->builder, ptr, wanted_type->type_ref, ""); } +static LLVMValueRef ir_render_widen_or_shorten(CodeGen *g, IrExecutable *executable, + IrInstructionWidenOrShorten *instruction) +{ + TypeTableEntry *actual_type = instruction->target->value.type; + // TODO instead of this logic, use the Noop instruction to change the type from + // enum_tag to the underlying int type + TypeTableEntry *int_type; + if (actual_type->id == TypeTableEntryIdEnum) { + TypeTableEntry *tag_type = actual_type->data.enumeration.tag_type; + assert(tag_type->id == TypeTableEntryIdEnumTag); + int_type = tag_type->data.enum_tag.int_type; + } else { + int_type = actual_type; + } + LLVMValueRef target_val = ir_llvm_value(g, instruction->target); + return gen_widen_or_shorten(g, ir_want_debug_safety(g, &instruction->base), int_type, + instruction->base.value.type, target_val); +} static LLVMValueRef ir_render_unreachable(CodeGen *g, IrExecutable *executable, IrInstructionUnreachable *unreachable_instruction) @@ -2309,6 +2321,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_struct_init(g, executable, (IrInstructionStructInit *)instruction); case IrInstructionIdPointerReinterpret: return ir_render_pointer_reinterpret(g, executable, (IrInstructionPointerReinterpret *)instruction); + case IrInstructionIdWidenOrShorten: + return ir_render_widen_or_shorten(g, executable, (IrInstructionWidenOrShorten *)instruction); case IrInstructionIdSwitchVar: zig_panic("TODO render switch var instruction to LLVM"); case IrInstructionIdContainerInitList: diff --git a/src/ir.cpp b/src/ir.cpp index 0da0e1f4c..fe603dc2d 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -455,6 +455,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionPointerReinterpr return IrInstructionIdPointerReinterpret; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionWidenOrShorten *) { + return IrInstructionIdWidenOrShorten; +} + template static T *ir_create_instruction(IrExecutable *exec, Scope *scope, AstNode *source_node) { T *special_instruction = allocate(1); @@ -1883,6 +1887,18 @@ static IrInstruction *ir_build_pointer_reinterpret(IrBuilder *irb, Scope *scope, return &instruction->base; } +static IrInstruction *ir_build_widen_or_shorten(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *target) +{ + IrInstructionWidenOrShorten *instruction = ir_build_instruction( + irb, scope, source_node); + instruction->target = target; + + ir_ref_instruction(target); + + return &instruction->base; +} + static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { results[ReturnKindUnconditional] = 0; results[ReturnKindError] = 0; @@ -4638,7 +4654,6 @@ static void eval_const_expr_implicit_cast(CastOp cast_op, case CastOpNoCast: zig_unreachable(); case CastOpNoop: - case CastOpWidenOrShorten: *const_val = *other_val; const_val->type = new_type; break; @@ -4684,10 +4699,6 @@ static void eval_const_expr_implicit_cast(CastOp cast_op, const_val->special = ConstValSpecialStatic; break; } - case CastOpEnumToInt: - bignum_init_unsigned(&const_val->data.x_bignum, other_val->data.x_enum.tag); - const_val->special = ConstValSpecialStatic; - break; } } static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, @@ -5181,6 +5192,50 @@ 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, TypeTableEntry *wanted_type) +{ + assert(wanted_type->id == TypeTableEntryIdInt); + + if (instr_is_comptime(target)) { + ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); + if (!val) + return ira->codegen->invalid_instruction; + IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, + source_instr->source_node, wanted_type, val->depends_on_compile_var); + init_const_unsigned_negative(&result->value, wanted_type, val->data.x_enum.tag, false); + 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; + return result; +} + +static IrInstruction *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *target, TypeTableEntry *wanted_type) +{ + assert(wanted_type->id == TypeTableEntryIdInt || wanted_type->id == TypeTableEntryIdFloat); + + if (instr_is_comptime(target)) { + ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); + if (!val) + return ira->codegen->invalid_instruction; + IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, + source_instr->source_node, wanted_type, val->depends_on_compile_var); + result->value = *val; + result->value.type = wanted_type; + 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; + return result; +} + + static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr, TypeTableEntry *wanted_type, IrInstruction *value) { @@ -5233,7 +5288,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst (wanted_type_canon->id == TypeTableEntryIdFloat && actual_type_canon->id == TypeTableEntryIdFloat)) { - return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpWidenOrShorten, false); + return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type); } // explicit cast from int to float @@ -5411,7 +5466,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst actual_type->id == TypeTableEntryIdEnum && actual_type->data.enumeration.gen_field_count == 0) { - return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpEnumToInt, false); + return ir_analyze_enum_to_int(ira, source_instr, value, wanted_type); } // explicit cast from undefined to anything @@ -9882,6 +9937,7 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi switch (instruction->id) { case IrInstructionIdInvalid: case IrInstructionIdPointerReinterpret: + case IrInstructionIdWidenOrShorten: case IrInstructionIdStructInit: case IrInstructionIdStructFieldPtr: case IrInstructionIdEnumFieldPtr: @@ -10188,6 +10244,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdTestComptime: case IrInstructionIdInitEnum: case IrInstructionIdPointerReinterpret: + case IrInstructionIdWidenOrShorten: return false; case IrInstructionIdAsm: { diff --git a/src/ir_print.cpp b/src/ir_print.cpp index be1089f89..f0dda04c6 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -923,11 +923,17 @@ static void ir_print_init_enum(IrPrint *irp, IrInstructionInitEnum *instruction) } static void ir_print_pointer_reinterpret(IrPrint *irp, IrInstructionPointerReinterpret *instruction) { - fprintf(irp->f, "(%s)(", buf_ptr(&instruction->base.value.type->name)); + fprintf(irp->f, "@pointerReinterpret("); ir_print_other_instruction(irp, instruction->ptr); fprintf(irp->f, ")"); } +static void ir_print_widen_or_shorten(IrPrint *irp, IrInstructionWidenOrShorten *instruction) { + fprintf(irp->f, "@widenOrShorten("); + ir_print_other_instruction(irp, instruction->target); + fprintf(irp->f, ")"); +} + static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_prefix(irp, instruction); switch (instruction->id) { @@ -1170,6 +1176,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdPointerReinterpret: ir_print_pointer_reinterpret(irp, (IrInstructionPointerReinterpret *)instruction); break; + case IrInstructionIdWidenOrShorten: + ir_print_widen_or_shorten(irp, (IrInstructionWidenOrShorten *)instruction); + break; } fprintf(irp->f, "\n"); } diff --git a/test/cases/enum_to_int.zig b/test/cases/enum_to_int.zig deleted file mode 100644 index 0be2c32a4..000000000 --- a/test/cases/enum_to_int.zig +++ /dev/null @@ -1,31 +0,0 @@ -const assert = @import("std").debug.assert; - -enum Number { - Zero, - One, - Two, - Three, - Four, -} - -fn enumToInt() { - @setFnTest(this, true); - - shouldEqual(false, Number.Zero, 0); - shouldEqual(false, Number.One, 1); - shouldEqual(false, Number.Two, 2); - shouldEqual(false, Number.Three, 3); - shouldEqual(false, Number.Four, 4); - - shouldEqual(true, Number.Zero, 0); - shouldEqual(true, Number.One, 1); - shouldEqual(true, Number.Two, 2); - shouldEqual(true, Number.Three, 3); - shouldEqual(true, Number.Four, 4); -} - -fn shouldEqual(inline static_eval: bool, n: Number, expected: usize) { - @setFnStaticEval(this, static_eval); - - assert(usize(n) == expected); -} diff --git a/test/cases3/enum.zig b/test/cases3/enum.zig index 5d204e853..316ef7189 100644 --- a/test/cases3/enum.zig +++ b/test/cases3/enum.zig @@ -73,6 +73,29 @@ const AnEnumWithPayload = enum { +const Number = enum { + Zero, + One, + Two, + Three, + Four, +}; + +fn enumToInt() { + @setFnTest(this); + + shouldEqual(Number.Zero, 0); + shouldEqual(Number.One, 1); + shouldEqual(Number.Two, 2); + shouldEqual(Number.Three, 3); + shouldEqual(Number.Four, 4); +} + +fn shouldEqual(n: Number, expected: usize) { + assert(usize(n) == expected); +} + +// TODO import from std fn assert(ok: bool) { if (!ok) @unreachable();