| author | |
| committer | |
| log | 4664f793dc3fed2254863a5b6efc302c095890cb |
| tree | 1c52e0597e181d09ebf199797894945de1eb25f4 |
| parent | 1b5d1877aea6c0db7fbe6f56ffe4412ce2cffff7 |
6 files changed, 124 insertions(+), 47 deletions(-)
src/all_types.hpp+7-2| ... | ... | @@ -429,14 +429,12 @@ enum CastOp { |
| 429 | 429 | CastOpNoop, // fn call expr is a cast, but does nothing |
| 430 | 430 | CastOpPtrToInt, |
| 431 | 431 | CastOpIntToPtr, |
| 432 | CastOpWidenOrShorten, | |
| 433 | 432 | CastOpErrToInt, |
| 434 | 433 | CastOpIntToFloat, |
| 435 | 434 | CastOpFloatToInt, |
| 436 | 435 | CastOpBoolToInt, |
| 437 | 436 | CastOpResizeSlice, |
| 438 | 437 | CastOpIntToEnum, |
| 439 | CastOpEnumToInt, | |
| 440 | 438 | CastOpBytesToSlice, |
| 441 | 439 | }; |
| 442 | 440 | |
| ... | ... | @@ -1454,6 +1452,7 @@ enum IrInstructionId { |
| 1454 | 1452 | IrInstructionIdTestComptime, |
| 1455 | 1453 | IrInstructionIdInitEnum, |
| 1456 | 1454 | IrInstructionIdPointerReinterpret, |
| 1455 | IrInstructionIdWidenOrShorten, | |
| 1457 | 1456 | }; |
| 1458 | 1457 | |
| 1459 | 1458 | struct IrInstruction { |
| ... | ... | @@ -2096,6 +2095,12 @@ struct IrInstructionPointerReinterpret { |
| 2096 | 2095 | IrInstruction *ptr; |
| 2097 | 2096 | }; |
| 2098 | 2097 | |
| 2098 | struct IrInstructionWidenOrShorten { | |
| 2099 | IrInstruction base; | |
| 2100 | ||
| 2101 | IrInstruction *target; | |
| 2102 | }; | |
| 2103 | ||
| 2099 | 2104 | enum LValPurpose { |
| 2100 | 2105 | LValPurposeNone, |
| 2101 | 2106 | LValPurposeAssign, |
src/codegen.cpp+20-6| ... | ... | @@ -973,9 +973,6 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, |
| 973 | 973 | return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, ""); |
| 974 | 974 | case CastOpIntToPtr: |
| 975 | 975 | return LLVMBuildIntToPtr(g->builder, expr_val, wanted_type->type_ref, ""); |
| 976 | case CastOpWidenOrShorten: | |
| 977 | return gen_widen_or_shorten(g, ir_want_debug_safety(g, &cast_instruction->base), | |
| 978 | actual_type, wanted_type, expr_val); | |
| 979 | 976 | case CastOpResizeSlice: |
| 980 | 977 | { |
| 981 | 978 | assert(cast_instruction->tmp_ptr); |
| ... | ... | @@ -1086,9 +1083,6 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, |
| 1086 | 1083 | case CastOpIntToEnum: |
| 1087 | 1084 | return gen_widen_or_shorten(g, ir_want_debug_safety(g, &cast_instruction->base), |
| 1088 | 1085 | actual_type, wanted_type->data.enumeration.tag_type, expr_val); |
| 1089 | case CastOpEnumToInt: | |
| 1090 | return gen_widen_or_shorten(g, ir_want_debug_safety(g, &cast_instruction->base), | |
| 1091 | actual_type->data.enumeration.tag_type, wanted_type, expr_val); | |
| 1092 | 1086 | } |
| 1093 | 1087 | zig_unreachable(); |
| 1094 | 1088 | } |
| ... | ... | @@ -1101,6 +1095,24 @@ static LLVMValueRef ir_render_pointer_reinterpret(CodeGen *g, IrExecutable *exec |
| 1101 | 1095 | return LLVMBuildBitCast(g->builder, ptr, wanted_type->type_ref, ""); |
| 1102 | 1096 | } |
| 1103 | 1097 | |
| 1098 | static LLVMValueRef ir_render_widen_or_shorten(CodeGen *g, IrExecutable *executable, | |
| 1099 | IrInstructionWidenOrShorten *instruction) | |
| 1100 | { | |
| 1101 | TypeTableEntry *actual_type = instruction->target->value.type; | |
| 1102 | // TODO instead of this logic, use the Noop instruction to change the type from | |
| 1103 | // enum_tag to the underlying int type | |
| 1104 | TypeTableEntry *int_type; | |
| 1105 | if (actual_type->id == TypeTableEntryIdEnum) { | |
| 1106 | TypeTableEntry *tag_type = actual_type->data.enumeration.tag_type; | |
| 1107 | assert(tag_type->id == TypeTableEntryIdEnumTag); | |
| 1108 | int_type = tag_type->data.enum_tag.int_type; | |
| 1109 | } else { | |
| 1110 | int_type = actual_type; | |
| 1111 | } | |
| 1112 | LLVMValueRef target_val = ir_llvm_value(g, instruction->target); | |
| 1113 | return gen_widen_or_shorten(g, ir_want_debug_safety(g, &instruction->base), int_type, | |
| 1114 | instruction->base.value.type, target_val); | |
| 1115 | } | |
| 1104 | 1116 | |
| 1105 | 1117 | static LLVMValueRef ir_render_unreachable(CodeGen *g, IrExecutable *executable, |
| 1106 | 1118 | IrInstructionUnreachable *unreachable_instruction) |
| ... | ... | @@ -2309,6 +2321,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, |
| 2309 | 2321 | return ir_render_struct_init(g, executable, (IrInstructionStructInit *)instruction); |
| 2310 | 2322 | case IrInstructionIdPointerReinterpret: |
| 2311 | 2323 | return ir_render_pointer_reinterpret(g, executable, (IrInstructionPointerReinterpret *)instruction); |
| 2324 | case IrInstructionIdWidenOrShorten: | |
| 2325 | return ir_render_widen_or_shorten(g, executable, (IrInstructionWidenOrShorten *)instruction); | |
| 2312 | 2326 | case IrInstructionIdSwitchVar: |
| 2313 | 2327 | zig_panic("TODO render switch var instruction to LLVM"); |
| 2314 | 2328 | case IrInstructionIdContainerInitList: |
src/ir.cpp+64-7| ... | ... | @@ -455,6 +455,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionPointerReinterpr |
| 455 | 455 | return IrInstructionIdPointerReinterpret; |
| 456 | 456 | } |
| 457 | 457 | |
| 458 | static constexpr IrInstructionId ir_instruction_id(IrInstructionWidenOrShorten *) { | |
| 459 | return IrInstructionIdWidenOrShorten; | |
| 460 | } | |
| 461 | ||
| 458 | 462 | template<typename T> |
| 459 | 463 | static T *ir_create_instruction(IrExecutable *exec, Scope *scope, AstNode *source_node) { |
| 460 | 464 | T *special_instruction = allocate<T>(1); |
| ... | ... | @@ -1883,6 +1887,18 @@ static IrInstruction *ir_build_pointer_reinterpret(IrBuilder *irb, Scope *scope, |
| 1883 | 1887 | return &instruction->base; |
| 1884 | 1888 | } |
| 1885 | 1889 | |
| 1890 | static IrInstruction *ir_build_widen_or_shorten(IrBuilder *irb, Scope *scope, AstNode *source_node, | |
| 1891 | IrInstruction *target) | |
| 1892 | { | |
| 1893 | IrInstructionWidenOrShorten *instruction = ir_build_instruction<IrInstructionWidenOrShorten>( | |
| 1894 | irb, scope, source_node); | |
| 1895 | instruction->target = target; | |
| 1896 | ||
| 1897 | ir_ref_instruction(target); | |
| 1898 | ||
| 1899 | return &instruction->base; | |
| 1900 | } | |
| 1901 | ||
| 1886 | 1902 | static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { |
| 1887 | 1903 | results[ReturnKindUnconditional] = 0; |
| 1888 | 1904 | results[ReturnKindError] = 0; |
| ... | ... | @@ -4638,7 +4654,6 @@ static void eval_const_expr_implicit_cast(CastOp cast_op, |
| 4638 | 4654 | case CastOpNoCast: |
| 4639 | 4655 | zig_unreachable(); |
| 4640 | 4656 | case CastOpNoop: |
| 4641 | case CastOpWidenOrShorten: | |
| 4642 | 4657 | *const_val = *other_val; |
| 4643 | 4658 | const_val->type = new_type; |
| 4644 | 4659 | break; |
| ... | ... | @@ -4684,10 +4699,6 @@ static void eval_const_expr_implicit_cast(CastOp cast_op, |
| 4684 | 4699 | const_val->special = ConstValSpecialStatic; |
| 4685 | 4700 | break; |
| 4686 | 4701 | } |
| 4687 | case CastOpEnumToInt: | |
| 4688 | bignum_init_unsigned(&const_val->data.x_bignum, other_val->data.x_enum.tag); | |
| 4689 | const_val->special = ConstValSpecialStatic; | |
| 4690 | break; | |
| 4691 | 4702 | } |
| 4692 | 4703 | } |
| 4693 | 4704 | 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 |
| 5181 | 5192 | return result; |
| 5182 | 5193 | } |
| 5183 | 5194 | |
| 5195 | static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *source_instr, | |
| 5196 | IrInstruction *target, TypeTableEntry *wanted_type) | |
| 5197 | { | |
| 5198 | assert(wanted_type->id == TypeTableEntryIdInt); | |
| 5199 | ||
| 5200 | if (instr_is_comptime(target)) { | |
| 5201 | ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); | |
| 5202 | if (!val) | |
| 5203 | return ira->codegen->invalid_instruction; | |
| 5204 | IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, | |
| 5205 | source_instr->source_node, wanted_type, val->depends_on_compile_var); | |
| 5206 | init_const_unsigned_negative(&result->value, wanted_type, val->data.x_enum.tag, false); | |
| 5207 | return result; | |
| 5208 | } | |
| 5209 | ||
| 5210 | IrInstruction *result = ir_build_widen_or_shorten(&ira->new_irb, source_instr->scope, | |
| 5211 | source_instr->source_node, target); | |
| 5212 | result->value.type = wanted_type; | |
| 5213 | return result; | |
| 5214 | } | |
| 5215 | ||
| 5216 | static IrInstruction *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInstruction *source_instr, | |
| 5217 | IrInstruction *target, TypeTableEntry *wanted_type) | |
| 5218 | { | |
| 5219 | assert(wanted_type->id == TypeTableEntryIdInt || wanted_type->id == TypeTableEntryIdFloat); | |
| 5220 | ||
| 5221 | if (instr_is_comptime(target)) { | |
| 5222 | ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); | |
| 5223 | if (!val) | |
| 5224 | return ira->codegen->invalid_instruction; | |
| 5225 | IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, | |
| 5226 | source_instr->source_node, wanted_type, val->depends_on_compile_var); | |
| 5227 | result->value = *val; | |
| 5228 | result->value.type = wanted_type; | |
| 5229 | return result; | |
| 5230 | } | |
| 5231 | ||
| 5232 | IrInstruction *result = ir_build_widen_or_shorten(&ira->new_irb, source_instr->scope, | |
| 5233 | source_instr->source_node, target); | |
| 5234 | result->value.type = wanted_type; | |
| 5235 | return result; | |
| 5236 | } | |
| 5237 | ||
| 5238 | ||
| 5184 | 5239 | static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr, |
| 5185 | 5240 | TypeTableEntry *wanted_type, IrInstruction *value) |
| 5186 | 5241 | { |
| ... | ... | @@ -5233,7 +5288,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst |
| 5233 | 5288 | (wanted_type_canon->id == TypeTableEntryIdFloat && |
| 5234 | 5289 | actual_type_canon->id == TypeTableEntryIdFloat)) |
| 5235 | 5290 | { |
| 5236 | return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpWidenOrShorten, false); | |
| 5291 | return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type); | |
| 5237 | 5292 | } |
| 5238 | 5293 | |
| 5239 | 5294 | // explicit cast from int to float |
| ... | ... | @@ -5411,7 +5466,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst |
| 5411 | 5466 | actual_type->id == TypeTableEntryIdEnum && |
| 5412 | 5467 | actual_type->data.enumeration.gen_field_count == 0) |
| 5413 | 5468 | { |
| 5414 | return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpEnumToInt, false); | |
| 5469 | return ir_analyze_enum_to_int(ira, source_instr, value, wanted_type); | |
| 5415 | 5470 | } |
| 5416 | 5471 | |
| 5417 | 5472 | // explicit cast from undefined to anything |
| ... | ... | @@ -9882,6 +9937,7 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi |
| 9882 | 9937 | switch (instruction->id) { |
| 9883 | 9938 | case IrInstructionIdInvalid: |
| 9884 | 9939 | case IrInstructionIdPointerReinterpret: |
| 9940 | case IrInstructionIdWidenOrShorten: | |
| 9885 | 9941 | case IrInstructionIdStructInit: |
| 9886 | 9942 | case IrInstructionIdStructFieldPtr: |
| 9887 | 9943 | case IrInstructionIdEnumFieldPtr: |
| ... | ... | @@ -10188,6 +10244,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 10188 | 10244 | case IrInstructionIdTestComptime: |
| 10189 | 10245 | case IrInstructionIdInitEnum: |
| 10190 | 10246 | case IrInstructionIdPointerReinterpret: |
| 10247 | case IrInstructionIdWidenOrShorten: | |
| 10191 | 10248 | return false; |
| 10192 | 10249 | case IrInstructionIdAsm: |
| 10193 | 10250 | { |
src/ir_print.cpp+10-1| ... | ... | @@ -923,11 +923,17 @@ static void ir_print_init_enum(IrPrint *irp, IrInstructionInitEnum *instruction) |
| 923 | 923 | } |
| 924 | 924 | |
| 925 | 925 | static void ir_print_pointer_reinterpret(IrPrint *irp, IrInstructionPointerReinterpret *instruction) { |
| 926 | fprintf(irp->f, "(%s)(", buf_ptr(&instruction->base.value.type->name)); | |
| 926 | fprintf(irp->f, "@pointerReinterpret("); | |
| 927 | 927 | ir_print_other_instruction(irp, instruction->ptr); |
| 928 | 928 | fprintf(irp->f, ")"); |
| 929 | 929 | } |
| 930 | 930 | |
| 931 | static void ir_print_widen_or_shorten(IrPrint *irp, IrInstructionWidenOrShorten *instruction) { | |
| 932 | fprintf(irp->f, "@widenOrShorten("); | |
| 933 | ir_print_other_instruction(irp, instruction->target); | |
| 934 | fprintf(irp->f, ")"); | |
| 935 | } | |
| 936 | ||
| 931 | 937 | static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 932 | 938 | ir_print_prefix(irp, instruction); |
| 933 | 939 | switch (instruction->id) { |
| ... | ... | @@ -1170,6 +1176,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 1170 | 1176 | case IrInstructionIdPointerReinterpret: |
| 1171 | 1177 | ir_print_pointer_reinterpret(irp, (IrInstructionPointerReinterpret *)instruction); |
| 1172 | 1178 | break; |
| 1179 | case IrInstructionIdWidenOrShorten: | |
| 1180 | ir_print_widen_or_shorten(irp, (IrInstructionWidenOrShorten *)instruction); | |
| 1181 | break; | |
| 1173 | 1182 | } |
| 1174 | 1183 | fprintf(irp->f, "\n"); |
| 1175 | 1184 | } |
test/cases/enum_to_int.zig deleted-31| ... | ... | @@ -1,31 +0,0 @@ |
| 1 | const assert = @import("std").debug.assert; | |
| 2 | ||
| 3 | enum Number { | |
| 4 | Zero, | |
| 5 | One, | |
| 6 | Two, | |
| 7 | Three, | |
| 8 | Four, | |
| 9 | } | |
| 10 | ||
| 11 | fn enumToInt() { | |
| 12 | @setFnTest(this, true); | |
| 13 | ||
| 14 | shouldEqual(false, Number.Zero, 0); | |
| 15 | shouldEqual(false, Number.One, 1); | |
| 16 | shouldEqual(false, Number.Two, 2); | |
| 17 | shouldEqual(false, Number.Three, 3); | |
| 18 | shouldEqual(false, Number.Four, 4); | |
| 19 | ||
| 20 | shouldEqual(true, Number.Zero, 0); | |
| 21 | shouldEqual(true, Number.One, 1); | |
| 22 | shouldEqual(true, Number.Two, 2); | |
| 23 | shouldEqual(true, Number.Three, 3); | |
| 24 | shouldEqual(true, Number.Four, 4); | |
| 25 | } | |
| 26 | ||
| 27 | fn shouldEqual(inline static_eval: bool, n: Number, expected: usize) { | |
| 28 | @setFnStaticEval(this, static_eval); | |
| 29 | ||
| 30 | assert(usize(n) == expected); | |
| 31 | } |
test/cases3/enum.zig+23| ... | ... | @@ -73,6 +73,29 @@ const AnEnumWithPayload = enum { |
| 73 | 73 | |
| 74 | 74 | |
| 75 | 75 | |
| 76 | const Number = enum { | |
| 77 | Zero, | |
| 78 | One, | |
| 79 | Two, | |
| 80 | Three, | |
| 81 | Four, | |
| 82 | }; | |
| 83 | ||
| 84 | fn enumToInt() { | |
| 85 | @setFnTest(this); | |
| 86 | ||
| 87 | shouldEqual(Number.Zero, 0); | |
| 88 | shouldEqual(Number.One, 1); | |
| 89 | shouldEqual(Number.Two, 2); | |
| 90 | shouldEqual(Number.Three, 3); | |
| 91 | shouldEqual(Number.Four, 4); | |
| 92 | } | |
| 93 | ||
| 94 | fn shouldEqual(n: Number, expected: usize) { | |
| 95 | assert(usize(n) == expected); | |
| 96 | } | |
| 97 | ||
| 98 | // TODO import from std | |
| 76 | 99 | fn assert(ok: bool) { |
| 77 | 100 | if (!ok) |
| 78 | 101 | @unreachable(); |