Commit 66a83d8738

Andrew Kelley <superjoe30@gmail.com>
2016-12-26 22:34:18
IR: pass intToEnum test
1 parent c8a7ab7
src/all_types.hpp
@@ -437,7 +437,6 @@ enum CastOp {
     CastOpFloatToInt,
     CastOpBoolToInt,
     CastOpResizeSlice,
-    CastOpIntToEnum,
     CastOpBytesToSlice,
 };
 
@@ -1458,6 +1457,7 @@ enum IrInstructionId {
     IrInstructionIdWidenOrShorten,
     IrInstructionIdIntToPtr,
     IrInstructionIdPtrToInt,
+    IrInstructionIdIntToEnum,
 };
 
 struct IrInstruction {
@@ -2118,6 +2118,12 @@ struct IrInstructionIntToPtr {
     IrInstruction *target;
 };
 
+struct IrInstructionIntToEnum {
+    IrInstruction base;
+
+    IrInstruction *target;
+};
+
 enum LValPurpose {
     LValPurposeNone,
     LValPurposeAssign,
src/codegen.cpp
@@ -1075,10 +1075,6 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable,
             assert(wanted_type->id == TypeTableEntryIdInt);
             assert(actual_type->id == TypeTableEntryIdBool);
             return LLVMBuildZExt(g->builder, expr_val, wanted_type->type_ref, "");
-
-        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);
     }
     zig_unreachable();
 }
@@ -1122,6 +1118,24 @@ static LLVMValueRef ir_render_ptr_to_int(CodeGen *g, IrExecutable *executable, I
     return LLVMBuildPtrToInt(g->builder, target_val, wanted_type->type_ref, "");
 }
 
+static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutable *executable, IrInstructionIntToEnum *instruction) {
+    TypeTableEntry *wanted_type = instruction->base.value.type;
+    assert(wanted_type->id == TypeTableEntryIdEnum);
+    TypeTableEntry *tag_type = wanted_type->data.enumeration.tag_type;
+    TypeTableEntry *wanted_int_type;
+    if (tag_type->id == TypeTableEntryIdEnumTag) {
+        wanted_int_type = tag_type->data.enum_tag.int_type;
+    } else if (tag_type->id == TypeTableEntryIdInt) {
+        wanted_int_type = tag_type;
+    } else {
+        zig_unreachable();
+    }
+
+    LLVMValueRef target_val = ir_llvm_value(g, instruction->target);
+    return gen_widen_or_shorten(g, ir_want_debug_safety(g, &instruction->base),
+            instruction->target->value.type, wanted_int_type, target_val);
+}
+
 static LLVMValueRef ir_render_unreachable(CodeGen *g, IrExecutable *executable,
         IrInstructionUnreachable *unreachable_instruction)
 {
@@ -2359,6 +2373,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
             return ir_render_ptr_to_int(g, executable, (IrInstructionPtrToInt *)instruction);
         case IrInstructionIdIntToPtr:
             return ir_render_int_to_ptr(g, executable, (IrInstructionIntToPtr *)instruction);
+        case IrInstructionIdIntToEnum:
+            return ir_render_int_to_enum(g, executable, (IrInstructionIntToEnum *)instruction);
         case IrInstructionIdContainerInitList:
             return ir_render_container_init_list(g, executable, (IrInstructionContainerInitList *)instruction);
         case IrInstructionIdSwitchVar:
src/ir.cpp
@@ -468,6 +468,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionIntToPtr *) {
     return IrInstructionIdIntToPtr;
 }
 
+static constexpr IrInstructionId ir_instruction_id(IrInstructionIntToEnum *) {
+    return IrInstructionIdIntToEnum;
+}
+
 template<typename T>
 static T *ir_create_instruction(IrExecutable *exec, Scope *scope, AstNode *source_node) {
     T *special_instruction = allocate<T>(1);
@@ -1932,6 +1936,18 @@ static IrInstruction *ir_build_ptr_to_int(IrBuilder *irb, Scope *scope, AstNode
     return &instruction->base;
 }
 
+static IrInstruction *ir_build_int_to_enum(IrBuilder *irb, Scope *scope, AstNode *source_node,
+        IrInstruction *target)
+{
+    IrInstructionIntToEnum *instruction = ir_build_instruction<IrInstructionIntToEnum>(
+            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;
@@ -4720,16 +4736,6 @@ static void eval_const_expr_implicit_cast(CastOp cast_op,
             bignum_init_unsigned(&const_val->data.x_bignum, other_val->data.x_bool ? 1 : 0);
             const_val->special = ConstValSpecialStatic;
             break;
-        case CastOpIntToEnum:
-            {
-                uint64_t value = other_val->data.x_bignum.data.x_uint;
-                assert(new_type->id == TypeTableEntryIdEnum);
-                assert(value < new_type->data.enumeration.src_field_count);
-                const_val->data.x_enum.tag = value;
-                const_val->data.x_enum.payload = NULL;
-                const_val->special = ConstValSpecialStatic;
-                break;
-            }
     }
 }
 static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value,
@@ -5311,6 +5317,27 @@ static IrInstruction *ir_analyze_int_to_ptr(IrAnalyze *ira, IrInstruction *sourc
     return result;
 }
 
+static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *source_instr,
+        IrInstruction *target, TypeTableEntry *wanted_type)
+{
+    assert(wanted_type->id == TypeTableEntryIdEnum);
+
+    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.data.x_enum.tag = val->data.x_bignum.data.x_uint;
+        return result;
+    }
+
+    IrInstruction *result = ir_build_int_to_enum(&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)
 {
@@ -5533,7 +5560,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
         wanted_type->id == TypeTableEntryIdEnum &&
         wanted_type->data.enumeration.gen_field_count == 0)
     {
-        return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpIntToEnum, false);
+        return ir_analyze_int_to_enum(ira, source_instr, value, wanted_type);
     }
 
     // explicit cast from enum type with no payload to integer
@@ -10016,6 +10043,7 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
         case IrInstructionIdWidenOrShorten:
         case IrInstructionIdIntToPtr:
         case IrInstructionIdPtrToInt:
+        case IrInstructionIdIntToEnum:
         case IrInstructionIdStructInit:
         case IrInstructionIdStructFieldPtr:
         case IrInstructionIdEnumFieldPtr:
@@ -10325,6 +10353,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
         case IrInstructionIdWidenOrShorten:
         case IrInstructionIdPtrToInt:
         case IrInstructionIdIntToPtr:
+        case IrInstructionIdIntToEnum:
             return false;
         case IrInstructionIdAsm:
             {
src/ir_print.cpp
@@ -952,6 +952,12 @@ static void ir_print_int_to_ptr(IrPrint *irp, IrInstructionIntToPtr *instruction
     fprintf(irp->f, ")");
 }
 
+static void ir_print_int_to_enum(IrPrint *irp, IrInstructionIntToEnum *instruction) {
+    fprintf(irp->f, "@intToEnum(");
+    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) {
@@ -1203,6 +1209,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
         case IrInstructionIdIntToPtr:
             ir_print_int_to_ptr(irp, (IrInstructionIntToPtr *)instruction);
             break;
+        case IrInstructionIdIntToEnum:
+            ir_print_int_to_enum(irp, (IrInstructionIntToEnum *)instruction);
+            break;
     }
     fprintf(irp->f, "\n");
 }
test/cases/enum.zig
@@ -96,6 +96,23 @@ fn shouldEqual(n: Number, expected: usize) {
 }
 
 
+fn intToEnum() {
+    @setFnTest(this);
+
+    testIntToEnumEval(3);
+}
+fn testIntToEnumEval(x: i32) {
+    assert(IntToEnumNumber(x) == IntToEnumNumber.Three);
+}
+const IntToEnumNumber = enum {
+    Zero,
+    One,
+    Two,
+    Three,
+    Four,
+};
+
+
 // TODO import from std
 fn assert(ok: bool) {
     if (!ok)
test/self_hosted.zig
@@ -80,20 +80,3 @@ fn castSliceToU8Slice() {
     assert(bytes[10] == @maxValue(u8));
     assert(bytes[11] == @maxValue(u8));
 }
-
-// TODO not passing
-fn intToEnum() {
-    @setFnTest(this);
-
-    testIntToEnumEval(3);
-}
-fn testIntToEnumEval(x: i32) {
-    assert(IntToEnumNumber(x) == IntToEnumNumber.Three);
-}
-const IntToEnumNumber = enum {
-    Zero,
-    One,
-    Two,
-    Three,
-    Four,
-};