Commit 095591f0b0

Andrew Kelley <superjoe30@gmail.com>
2017-04-08 23:45:22
add enumTagName builtin function
closes #299
1 parent 7611ed3
doc/langref.md
@@ -625,3 +625,7 @@ Converts a pointer of one type to a pointer of another type.
 ### @intToPtr(comptime DestType: type, int: usize) -> DestType
 
 Converts an integer to a pointer. To convert the other way, use `usize(ptr)`.
+
+### @enumTagName(value: var) -> []const u8
+
+Converts an enum tag name to a slice of bytes. Example:
src/all_types.hpp
@@ -966,6 +966,8 @@ struct TypeTableEntryEnum {
 struct TypeTableEntryEnumTag {
     TypeTableEntry *enum_type;
     TypeTableEntry *int_type;
+    bool generate_name_table;
+    LLVMValueRef name_table;
 };
 
 struct TypeTableEntryUnion {
@@ -1200,6 +1202,7 @@ enum BuiltinFnId {
     BuiltinFnIdPanic,
     BuiltinFnIdPtrCast,
     BuiltinFnIdIntToPtr,
+    BuiltinFnIdEnumTagName,
 };
 
 struct BuiltinFnEntry {
@@ -1464,6 +1467,8 @@ struct CodeGen {
 
     Buf global_asm;
     ZigList<Buf *> link_objects;
+
+    ZigList<TypeTableEntry *> name_table_enums;
 };
 
 enum VarLinkage {
@@ -1742,6 +1747,7 @@ enum IrInstructionId {
     IrInstructionIdSetGlobalLinkage,
     IrInstructionIdDeclRef,
     IrInstructionIdPanic,
+    IrInstructionIdEnumTagName,
 };
 
 struct IrInstruction {
@@ -2481,6 +2487,12 @@ struct IrInstructionPanic {
     IrInstruction *msg;
 };
 
+struct IrInstructionEnumTagName {
+    IrInstruction base;
+
+    IrInstruction *target;
+};
+
 static const size_t slice_ptr_index = 0;
 static const size_t slice_len_index = 1;
 
src/analyze.cpp
@@ -1181,7 +1181,7 @@ bool type_is_invalid(TypeTableEntry *type_entry) {
 }
 
 
-static TypeTableEntry *create_enum_tag_type(CodeGen *g, TypeTableEntry *enum_type, TypeTableEntry *int_type) {
+TypeTableEntry *create_enum_tag_type(CodeGen *g, TypeTableEntry *enum_type, TypeTableEntry *int_type) {
     TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnumTag);
 
     buf_resize(&entry->name, 0);
src/analyze.hpp
@@ -149,5 +149,6 @@ void init_const_undefined(CodeGen *g, ConstExprValue *const_val);
 TypeTableEntry *make_int_type(CodeGen *g, bool is_signed, size_t size_in_bits);
 ConstParent *get_const_val_parent(ConstExprValue *value);
 FnTableEntry *get_extern_panic_fn(CodeGen *g);
+TypeTableEntry *create_enum_tag_type(CodeGen *g, TypeTableEntry *enum_type, TypeTableEntry *int_type);
 
 #endif
src/codegen.cpp
@@ -2134,6 +2134,29 @@ static LLVMValueRef ir_render_err_name(CodeGen *g, IrExecutable *executable, IrI
     return LLVMBuildInBoundsGEP(g->builder, g->err_name_table, indices, 2, "");
 }
 
+static LLVMValueRef ir_render_enum_tag_name(CodeGen *g, IrExecutable *executable,
+        IrInstructionEnumTagName *instruction)
+{
+    TypeTableEntry *enum_tag_type = instruction->target->value.type;
+    assert(enum_tag_type->data.enum_tag.generate_name_table);
+
+    LLVMValueRef enum_tag_value = ir_llvm_value(g, instruction->target);
+    if (ir_want_debug_safety(g, &instruction->base)) {
+        TypeTableEntry *enum_type = enum_tag_type->data.enum_tag.enum_type;
+        size_t field_count = enum_type->data.enumeration.src_field_count;
+        LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(enum_tag_value));
+        LLVMValueRef end_val = LLVMConstInt(LLVMTypeOf(enum_tag_value), field_count, false);
+        add_bounds_check(g, enum_tag_value, LLVMIntUGE, zero, LLVMIntULT, end_val);
+    }
+
+    LLVMValueRef indices[] = {
+        LLVMConstNull(g->builtin_types.entry_usize->type_ref),
+        enum_tag_value,
+    };
+    return LLVMBuildInBoundsGEP(g->builder, enum_tag_type->data.enum_tag.name_table, indices, 2, "");
+}
+
+
 static LLVMAtomicOrdering to_LLVMAtomicOrdering(AtomicOrder atomic_order) {
     switch (atomic_order) {
         case AtomicOrderUnordered: return LLVMAtomicOrderingUnordered;
@@ -2830,6 +2853,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
             return ir_render_container_init_list(g, executable, (IrInstructionContainerInitList *)instruction);
         case IrInstructionIdPanic:
             return ir_render_panic(g, executable, (IrInstructionPanic *)instruction);
+        case IrInstructionIdEnumTagName:
+            return ir_render_enum_tag_name(g, executable, (IrInstructionEnumTagName *)instruction);
     }
     zig_unreachable();
 }
@@ -3390,6 +3415,46 @@ static void generate_error_name_table(CodeGen *g) {
     LLVMSetUnnamedAddr(g->err_name_table, true);
 }
 
+static void generate_enum_name_tables(CodeGen *g) {
+    TypeTableEntry *str_type = get_slice_type(g, g->builtin_types.entry_u8, true);
+    TypeTableEntry *u8_ptr_type = str_type->data.structure.fields[0].type_entry;
+
+    for (size_t enum_i = 0; enum_i < g->name_table_enums.length; enum_i += 1) {
+        TypeTableEntry *enum_tag_type = g->name_table_enums.at(enum_i);
+        assert(enum_tag_type->id == TypeTableEntryIdEnumTag);
+        TypeTableEntry *enum_type = enum_tag_type->data.enum_tag.enum_type;
+
+        size_t field_count = enum_type->data.enumeration.src_field_count;
+        LLVMValueRef *values = allocate<LLVMValueRef>(field_count);
+        for (size_t field_i = 0; field_i < field_count; field_i += 1) {
+            Buf *name = enum_type->data.enumeration.fields[field_i].name;
+
+            LLVMValueRef str_init = LLVMConstString(buf_ptr(name), buf_len(name), true);
+            LLVMValueRef str_global = LLVMAddGlobal(g->module, LLVMTypeOf(str_init), "");
+            LLVMSetInitializer(str_global, str_init);
+            LLVMSetLinkage(str_global, LLVMPrivateLinkage);
+            LLVMSetGlobalConstant(str_global, true);
+            LLVMSetUnnamedAddr(str_global, true);
+
+            LLVMValueRef fields[] = {
+                LLVMConstBitCast(str_global, u8_ptr_type->type_ref),
+                LLVMConstInt(g->builtin_types.entry_usize->type_ref, buf_len(name), false),
+            };
+            values[field_i] = LLVMConstNamedStruct(str_type->type_ref, fields, 2);
+        }
+
+        LLVMValueRef name_table_init = LLVMConstArray(str_type->type_ref, values, field_count);
+
+        Buf *table_name = buf_sprintf("%s_name_table", buf_ptr(&enum_type->name));
+        LLVMValueRef name_table = LLVMAddGlobal(g->module, LLVMTypeOf(name_table_init), buf_ptr(table_name));
+        LLVMSetInitializer(name_table, name_table_init);
+        LLVMSetLinkage(name_table, LLVMPrivateLinkage);
+        LLVMSetGlobalConstant(name_table, true);
+        LLVMSetUnnamedAddr(name_table, true);
+        enum_tag_type->data.enum_tag.name_table = name_table;
+    }
+}
+
 static void build_all_basic_blocks(CodeGen *g, FnTableEntry *fn) {
     IrExecutable *executable = &fn->analyzed_executable;
     assert(executable->basic_block_list.length > 0);
@@ -3428,6 +3493,7 @@ static void do_code_gen(CodeGen *g) {
 
     delete_unused_builtin_fns(g);
     generate_error_name_table(g);
+    generate_enum_name_tables(g);
 
     // Generate module level variables
     for (size_t i = 0; i < g->global_vars.length; i += 1) {
@@ -3800,7 +3866,8 @@ static const GlobalLinkageValue global_linkage_values[] = {
 static void init_enum_debug_info(CodeGen *g, TypeTableEntry *enum_type) {
     uint32_t field_count = enum_type->data.enumeration.src_field_count;
 
-    TypeTableEntry *tag_type_entry = get_smallest_unsigned_int_type(g, field_count);
+    TypeTableEntry *tag_int_type = get_smallest_unsigned_int_type(g, field_count);
+    TypeTableEntry *tag_type_entry = create_enum_tag_type(g, enum_type, tag_int_type);
     enum_type->data.enumeration.tag_type = tag_type_entry;
 
     ZigLLVMDIEnumerator **di_enumerators = allocate<ZigLLVMDIEnumerator*>(field_count);
@@ -4327,6 +4394,7 @@ static void define_builtin_fns(CodeGen *g) {
     create_builtin_fn(g, BuiltinFnIdPanic, "panic", 1);
     create_builtin_fn(g, BuiltinFnIdPtrCast, "ptrcast", 2);
     create_builtin_fn(g, BuiltinFnIdIntToPtr, "intToPtr", 2);
+    create_builtin_fn(g, BuiltinFnIdEnumTagName, "enumTagName", 1);
 }
 
 static void add_compile_var(CodeGen *g, const char *name, ConstExprValue *value) {
src/ir.cpp
@@ -541,6 +541,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionPanic *) {
     return IrInstructionIdPanic;
 }
 
+static constexpr IrInstructionId ir_instruction_id(IrInstructionEnumTagName *) {
+    return IrInstructionIdEnumTagName;
+}
+
 template<typename T>
 static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
     T *special_instruction = allocate<T>(1);
@@ -2130,6 +2134,17 @@ static IrInstruction *ir_build_panic(IrBuilder *irb, Scope *scope, AstNode *sour
     return &instruction->base;
 }
 
+static IrInstruction *ir_build_enum_tag_name(IrBuilder *irb, Scope *scope, AstNode *source_node,
+        IrInstruction *target)
+{
+    IrInstructionEnumTagName *instruction = ir_build_instruction<IrInstructionEnumTagName>(irb, scope, source_node);
+    instruction->target = target;
+
+    ir_ref_instruction(target, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
 static IrInstruction *ir_instruction_br_get_dep(IrInstructionBr *instruction, size_t index) {
     return nullptr;
 }
@@ -2787,6 +2802,13 @@ static IrInstruction *ir_instruction_panic_get_dep(IrInstructionPanic *instructi
     }
 }
 
+static IrInstruction *ir_instruction_enumtagname_get_dep(IrInstructionEnumTagName *instruction, size_t index) {
+    switch (index) {
+        case 0: return instruction->target;
+        default: return nullptr;
+    }
+}
+
 static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t index) {
     switch (instruction->id) {
         case IrInstructionIdInvalid:
@@ -2975,6 +2997,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t
             return ir_instruction_declref_get_dep((IrInstructionDeclRef *) instruction, index);
         case IrInstructionIdPanic:
             return ir_instruction_panic_get_dep((IrInstructionPanic *) instruction, index);
+        case IrInstructionIdEnumTagName:
+            return ir_instruction_enumtagname_get_dep((IrInstructionEnumTagName *) instruction, index);
     }
     zig_unreachable();
 }
@@ -4239,6 +4263,16 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
 
                 return ir_build_int_to_ptr(irb, scope, node, arg0_value, arg1_value);
             }
+        case BuiltinFnIdEnumTagName:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_instruction)
+                    return arg0_value;
+
+                IrInstruction *actual_tag = ir_build_enum_tag(irb, scope, node, arg0_value);
+                return ir_build_enum_tag_name(irb, scope, node, actual_tag);
+            }
     }
     zig_unreachable();
 }
@@ -10290,6 +10324,8 @@ static IrInstruction *ir_analyze_enum_tag(IrAnalyze *ira, IrInstruction *source_
         return ira->codegen->invalid_instruction;
     }
 
+    TypeTableEntry *tag_type = value->value.type->data.enumeration.tag_type;
+
     if (instr_is_comptime(value)) {
         ConstExprValue *val = ir_resolve_const(ira, value, UndefBad);
         if (!val)
@@ -10297,13 +10333,15 @@ static IrInstruction *ir_analyze_enum_tag(IrAnalyze *ira, IrInstruction *source_
 
         IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb,
                 source_instr->scope, source_instr->source_node);
-        const_instruction->base.value.type = value->value.type->data.enumeration.tag_type;
+        const_instruction->base.value.type = tag_type;
         const_instruction->base.value.special = ConstValSpecialStatic;
         bignum_init_unsigned(&const_instruction->base.value.data.x_bignum, val->data.x_enum.tag);
         return &const_instruction->base;
     }
 
-    zig_panic("TODO runtime enum tag instruction");
+    IrInstruction *result = ir_build_enum_tag(&ira->new_irb, source_instr->scope, source_instr->source_node, value);
+    result->value.type = tag_type;
+    return result;
 }
 
 static TypeTableEntry *ir_analyze_instruction_switch_br(IrAnalyze *ira,
@@ -11097,6 +11135,35 @@ static TypeTableEntry *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstruc
     return str_type;
 }
 
+static TypeTableEntry *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrInstructionEnumTagName *instruction) {
+    IrInstruction *target = instruction->target->other;
+    if (type_is_invalid(target->value.type))
+        return ira->codegen->builtin_types.entry_invalid;
+
+    assert(target->value.type->id == TypeTableEntryIdEnumTag);
+
+    if (instr_is_comptime(target)) {
+        TypeTableEntry *enum_type = target->value.type->data.enum_tag.enum_type;
+        uint64_t tag_value = target->value.data.x_bignum.data.x_uint;
+        TypeEnumField *field = &enum_type->data.enumeration.fields[tag_value];
+        ConstExprValue *array_val = create_const_str_lit(ira->codegen, field->name);
+        ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
+        init_const_slice(ira->codegen, out_val, array_val, 0, buf_len(field->name), true);
+        return out_val->type;
+    }
+
+    if (!target->value.type->data.enum_tag.generate_name_table) {
+        target->value.type->data.enum_tag.generate_name_table = true;
+        ira->codegen->name_table_enums.append(target->value.type);
+    }
+
+    IrInstruction *result = ir_build_enum_tag_name(&ira->new_irb, instruction->base.scope,
+            instruction->base.source_node, target);
+    ir_link_new_instruction(result, &instruction->base);
+    result->value.type = get_slice_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true);
+    return result->value.type;
+}
+
 static TypeTableEntry *ir_analyze_instruction_type_name(IrAnalyze *ira, IrInstructionTypeName *instruction) {
     IrInstruction *type_value = instruction->type_value->other;
     TypeTableEntry *type_entry = ir_resolve_type(ira, type_value);
@@ -12641,6 +12708,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
             return ir_analyze_instruction_ptr_cast(ira, (IrInstructionPtrCast *)instruction);
         case IrInstructionIdIntToPtr:
             return ir_analyze_instruction_int_to_ptr(ira, (IrInstructionIntToPtr *)instruction);
+        case IrInstructionIdEnumTagName:
+            return ir_analyze_instruction_enum_tag_name(ira, (IrInstructionEnumTagName *)instruction);
         case IrInstructionIdMaybeWrap:
         case IrInstructionIdErrWrapCode:
         case IrInstructionIdErrWrapPayload:
@@ -12792,7 +12861,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
         case IrInstructionIdRef:
         case IrInstructionIdMinValue:
         case IrInstructionIdMaxValue:
-        case IrInstructionIdErrName:
         case IrInstructionIdEmbedFile:
         case IrInstructionIdDivExact:
         case IrInstructionIdTruncate:
@@ -12819,9 +12887,11 @@ bool ir_has_side_effects(IrInstruction *instruction) {
         case IrInstructionIdIntToErr:
         case IrInstructionIdErrToInt:
         case IrInstructionIdTestType:
-        case IrInstructionIdTypeName:
         case IrInstructionIdCanImplicitCast:
         case IrInstructionIdDeclRef:
+        case IrInstructionIdErrName:
+        case IrInstructionIdTypeName:
+        case IrInstructionIdEnumTagName:
             return false;
         case IrInstructionIdAsm:
             {
src/ir_print.cpp
@@ -816,15 +816,18 @@ static void ir_print_check_switch_prongs(IrPrint *irp, IrInstructionCheckSwitchP
 }
 
 static void ir_print_test_type(IrPrint *irp, IrInstructionTestType *instruction) {
-    fprintf(irp->f, "@testType(");
+    fprintf(irp->f, "testtype ");
     ir_print_other_instruction(irp, instruction->type_value);
-    fprintf(irp->f, ")");
 }
 
 static void ir_print_type_name(IrPrint *irp, IrInstructionTypeName *instruction) {
-    fprintf(irp->f, "@typeName(");
+    fprintf(irp->f, "typename ");
     ir_print_other_instruction(irp, instruction->type_value);
-    fprintf(irp->f, ")");
+}
+
+static void ir_print_enum_tag_name(IrPrint *irp, IrInstructionEnumTagName *instruction) {
+    fprintf(irp->f, "enumtagname ");
+    ir_print_other_instruction(irp, instruction->target);
 }
 
 static void ir_print_can_implicit_cast(IrPrint *irp, IrInstructionCanImplicitCast *instruction) {
@@ -1131,6 +1134,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
         case IrInstructionIdTypeName:
             ir_print_type_name(irp, (IrInstructionTypeName *)instruction);
             break;
+        case IrInstructionIdEnumTagName:
+            ir_print_enum_tag_name(irp, (IrInstructionEnumTagName *)instruction);
+            break;
         case IrInstructionIdCanImplicitCast:
             ir_print_can_implicit_cast(irp, (IrInstructionCanImplicitCast *)instruction);
             break;
std/os/index.zig
@@ -272,7 +272,7 @@ pub fn posixExecve(exe_path: []const u8, argv: []const []const u8, env_map: &con
         return posixExecveErrnoToErr(posix.getErrno(posix.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr)));
     }
 
-    const PATH = getEnv("PATH") ?? ([]const u8)("/usr/local/bin:/bin/:/usr/bin"); // TODO issue #299
+    const PATH = getEnv("PATH") ?? "/usr/local/bin:/bin/:/usr/bin";
     // PATH.len because it is >= the largest search_path
     // +1 for the / to join the search path and exe_path
     // +1 for the null terminating byte
std/build.zig
@@ -137,13 +137,13 @@ pub const Builder = struct {
                 Target.Native => {},
                 Target.Cross => |cross_target| {
                     %return zig_args.append("--target-arch");
-                    %return zig_args.append(targetArchName(cross_target.arch));
+                    %return zig_args.append(@enumTagName(cross_target.arch));
 
                     %return zig_args.append("--target-os");
-                    %return zig_args.append(targetOsName(cross_target.os));
+                    %return zig_args.append(@enumTagName(cross_target.os));
 
                     %return zig_args.append("--target-environ");
-                    %return zig_args.append(targetEnvironName(cross_target.environ));
+                    %return zig_args.append(@enumTagName(cross_target.environ));
                 },
             }
 
@@ -458,131 +458,3 @@ fn printInvocation(exe_name: []const u8, args: &const List([]const u8)) {
     }
     %%io.stderr.printf("\n");
 }
-
-// TODO issue #299
-fn targetOsName(target_os: Os) -> []const u8 {
-    return switch (target_os) {
-        Os.freestanding => "freestanding",
-        Os.cloudabi => "cloudabi",
-        Os.darwin => "darwin",
-        Os.dragonfly => "dragonfly",
-        Os.freebsd => "freebsd",
-        Os.ios => "ios",
-        Os.kfreebsd => "kfreebsd",
-        Os.linux => "linux",
-        Os.lv2 => "lv2",
-        Os.macosx => "macosx",
-        Os.netbsd => "netbsd",
-        Os.openbsd => "openbsd",
-        Os.solaris => "solaris",
-        Os.windows => "windows",
-        Os.haiku => "haiku",
-        Os.minix => "minix",
-        Os.rtems => "rtems",
-        Os.nacl => "nacl",
-        Os.cnk => "cnk",
-        Os.bitrig => "bitrig",
-        Os.aix => "aix",
-        Os.cuda => "cuda",
-        Os.nvcl => "nvcl",
-        Os.amdhsa => "amdhsa",
-        Os.ps4 => "ps4",
-        Os.elfiamcu => "elfiamcu",
-        Os.tvos => "tvos",
-        Os.watchos => "watchos",
-        Os.mesa3d => "mesa3d",
-    };
-}
-
-// TODO issue #299
-fn targetArchName(target_arch: Arch) -> []const u8 {
-    return switch (target_arch) {
-        Arch.armv8_2a => "armv8_2a",
-        Arch.armv8_1a => "armv8_1a",
-        Arch.armv8 => "armv8",
-        Arch.armv8m_baseline => "armv8m_baseline",
-        Arch.armv8m_mainline => "armv8m_mainline",
-        Arch.armv7 => "armv7",
-        Arch.armv7em => "armv7em",
-        Arch.armv7m => "armv7m",
-        Arch.armv7s => "armv7s",
-        Arch.armv7k => "armv7k",
-        Arch.armv6 => "armv6",
-        Arch.armv6m => "armv6m",
-        Arch.armv6k => "armv6k",
-        Arch.armv6t2 => "armv6t2",
-        Arch.armv5 => "armv5",
-        Arch.armv5te => "armv5te",
-        Arch.armv4t => "armv4t",
-        Arch.armeb => "armeb",
-        Arch.aarch64 => "aarch64",
-        Arch.aarch64_be => "aarch64_be",
-        Arch.avr => "avr",
-        Arch.bpfel => "bpfel",
-        Arch.bpfeb => "bpfeb",
-        Arch.hexagon => "hexagon",
-        Arch.mips => "mips",
-        Arch.mipsel => "mipsel",
-        Arch.mips64 => "mips64",
-        Arch.mips64el => "mips64el",
-        Arch.msp430 => "msp430",
-        Arch.powerpc => "powerpc",
-        Arch.powerpc64 => "powerpc64",
-        Arch.powerpc64le => "powerpc64le",
-        Arch.r600 => "r600",
-        Arch.amdgcn => "amdgcn",
-        Arch.sparc => "sparc",
-        Arch.sparcv9 => "sparcv9",
-        Arch.sparcel => "sparcel",
-        Arch.s390x => "s390x",
-        Arch.tce => "tce",
-        Arch.thumb => "thumb",
-        Arch.thumbeb => "thumbeb",
-        Arch.i386 => "i386",
-        Arch.x86_64 => "x86_64",
-        Arch.xcore => "xcore",
-        Arch.nvptx => "nvptx",
-        Arch.nvptx64 => "nvptx64",
-        Arch.le32 => "le32",
-        Arch.le64 => "le64",
-        Arch.amdil => "amdil",
-        Arch.amdil64 => "amdil64",
-        Arch.hsail => "hsail",
-        Arch.hsail64 => "hsail64",
-        Arch.spir => "spir",
-        Arch.spir64 => "spir64",
-        Arch.kalimbav3 => "kalimbav3",
-        Arch.kalimbav4 => "kalimbav4",
-        Arch.kalimbav5 => "kalimbav5",
-        Arch.shave => "shave",
-        Arch.lanai => "lanai",
-        Arch.wasm32 => "wasm32",
-        Arch.wasm64 => "wasm64",
-        Arch.renderscript32 => "renderscript32",
-        Arch.renderscript64 => "renderscript64",
-    };
-}
-
-// TODO issue #299
-fn targetEnvironName(target_environ: Environ) -> []const u8 {
-    return switch (target_environ) {
-        Environ.gnu => "gnu",
-        Environ.gnuabi64 => "gnuabi64",
-        Environ.gnueabi => "gnueabi",
-        Environ.gnueabihf => "gnueabihf",
-        Environ.gnux32 => "gnux32",
-        Environ.code16 => "code16",
-        Environ.eabi => "eabi",
-        Environ.eabihf => "eabihf",
-        Environ.android => "android",
-        Environ.musl => "musl",
-        Environ.musleabi => "musleabi",
-        Environ.musleabihf => "musleabihf",
-        Environ.msvc => "msvc",
-        Environ.itanium => "itanium",
-        Environ.cygnus => "cygnus",
-        Environ.amdopencl => "amdopencl",
-        Environ.coreclr => "coreclr",
-    };
-}
-
test/cases/enum.zig
@@ -1,4 +1,5 @@
 const assert = @import("std").debug.assert;
+const mem = @import("std").mem;
 
 test "enumType" {
     const foo1 = Foo.One {13};
@@ -103,3 +104,19 @@ const IntToEnumNumber = enum {
     Three,
     Four,
 };
+
+
+test "enumTagName builtin function" {
+    assert(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three"));
+    comptime assert(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three"));
+}
+
+fn testEnumTagNameBare(n: BareNumber) -> []const u8 {
+    return @enumTagName(n);
+}
+
+const BareNumber = enum {
+    One,
+    Two,
+    Three,
+};