Commit 634e8713c3

Andrew Kelley <superjoe30@gmail.com>
2017-11-07 04:07:19
add @memberType and @memberName builtin functions
see #383 there is a plan to unify most of the reflection into 2 builtin functions, as outlined in the above issue, but this gives us needed features for now, and we can iterate on the design in future commits
1 parent f0dafd3
src/all_types.hpp
@@ -1211,6 +1211,8 @@ enum BuiltinFnId {
     BuiltinFnIdMaxValue,
     BuiltinFnIdMinValue,
     BuiltinFnIdMemberCount,
+    BuiltinFnIdMemberType,
+    BuiltinFnIdMemberName,
     BuiltinFnIdTypeof,
     BuiltinFnIdAddWithOverflow,
     BuiltinFnIdSubWithOverflow,
@@ -1845,6 +1847,8 @@ enum IrInstructionId {
     IrInstructionIdMemcpy,
     IrInstructionIdSlice,
     IrInstructionIdMemberCount,
+    IrInstructionIdMemberType,
+    IrInstructionIdMemberName,
     IrInstructionIdBreakpoint,
     IrInstructionIdReturnAddress,
     IrInstructionIdFrameAddress,
@@ -2408,6 +2412,20 @@ struct IrInstructionMemberCount {
     IrInstruction *container;
 };
 
+struct IrInstructionMemberType {
+    IrInstruction base;
+
+    IrInstruction *container_type;
+    IrInstruction *member_index;
+};
+
+struct IrInstructionMemberName {
+    IrInstruction base;
+
+    IrInstruction *container_type;
+    IrInstruction *member_index;
+};
+
 struct IrInstructionBreakpoint {
     IrInstruction base;
 };
src/analyze.cpp
@@ -1366,119 +1366,140 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) {
     enum_type->data.enumeration.union_size_bytes = biggest_size_in_bits / 8;
     enum_type->data.enumeration.most_aligned_union_member = most_aligned_union_member;
 
-    if (!enum_type->data.enumeration.is_invalid) {
-        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;
-
-        uint64_t align_of_tag_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_int_type->type_ref);
-
-        if (most_aligned_union_member) {
-            // create llvm type for union
-            uint64_t padding_in_bits = biggest_size_in_bits - size_of_most_aligned_member_in_bits;
-            LLVMTypeRef union_type_ref;
-            if (padding_in_bits > 0) {
-                TypeTableEntry *u8_type = get_int_type(g, false, 8);
-                TypeTableEntry *padding_array = get_array_type(g, u8_type, padding_in_bits / 8);
-                LLVMTypeRef union_element_types[] = {
-                    most_aligned_union_member->type_ref,
-                    padding_array->type_ref,
-                };
-                union_type_ref = LLVMStructType(union_element_types, 2, false);
-            } else {
-                union_type_ref = most_aligned_union_member->type_ref;
-            }
-            enum_type->data.enumeration.union_type_ref = union_type_ref;
+    if (enum_type->data.enumeration.is_invalid)
+        return;
 
-            assert(8*LLVMABIAlignmentOfType(g->target_data_ref, union_type_ref) >= biggest_align_in_bits);
-            assert(8*LLVMStoreSizeOfType(g->target_data_ref, union_type_ref) >= biggest_size_in_bits);
+    if (enum_type->zero_bits) {
+        enum_type->type_ref = LLVMVoidType();
 
-            if (align_of_tag_in_bits >= biggest_align_in_bits) {
-                enum_type->data.enumeration.gen_tag_index = 0;
-                enum_type->data.enumeration.gen_union_index = 1;
-            } else {
-                enum_type->data.enumeration.gen_union_index = 0;
-                enum_type->data.enumeration.gen_tag_index = 1;
-            }
+        uint64_t debug_size_in_bits = 0;
+        uint64_t debug_align_in_bits = 0;
+        ZigLLVMDIType **di_root_members = nullptr;
+        size_t debug_member_count = 0;
+        ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
+                ZigLLVMFileToScope(import->di_file),
+                buf_ptr(&enum_type->name),
+                import->di_file, (unsigned)(decl_node->line + 1),
+                debug_size_in_bits,
+                debug_align_in_bits,
+                0, nullptr, di_root_members, (int)debug_member_count, 0, nullptr, "");
 
-            // create llvm type for root struct
-            LLVMTypeRef root_struct_element_types[2];
-            root_struct_element_types[enum_type->data.enumeration.gen_tag_index] = tag_type_entry->type_ref;
-            root_struct_element_types[enum_type->data.enumeration.gen_union_index] = union_type_ref;
-            LLVMStructSetBody(enum_type->type_ref, root_struct_element_types, 2, false);
-
-            // create debug type for tag
-            uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type_entry->type_ref);
-            uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_type_entry->type_ref);
-            ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder,
-                    ZigLLVMTypeToScope(enum_type->di_type), "AnonEnum",
-                    import->di_file, (unsigned)(decl_node->line + 1),
-                    tag_debug_size_in_bits, tag_debug_align_in_bits, di_enumerators, field_count,
-                    tag_type_entry->di_type, "");
-
-            // create debug type for union
-            ZigLLVMDIType *union_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder,
-                    ZigLLVMTypeToScope(enum_type->di_type), "AnonUnion",
-                    import->di_file, (unsigned)(decl_node->line + 1),
-                    biggest_size_in_bits, biggest_align_in_bits, 0, union_inner_di_types,
-                    gen_field_count, 0, "");
-
-            // create debug types for members of root struct
-            uint64_t tag_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, enum_type->type_ref,
-                    enum_type->data.enumeration.gen_tag_index);
-            ZigLLVMDIType *tag_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder,
-                    ZigLLVMTypeToScope(enum_type->di_type), "tag_field",
-                    import->di_file, (unsigned)(decl_node->line + 1),
-                    tag_debug_size_in_bits,
-                    tag_debug_align_in_bits,
-                    tag_offset_in_bits,
-                    0, tag_di_type);
-
-            uint64_t union_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, enum_type->type_ref,
-                    enum_type->data.enumeration.gen_union_index);
-            ZigLLVMDIType *union_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder,
-                    ZigLLVMTypeToScope(enum_type->di_type), "union_field",
-                    import->di_file, (unsigned)(decl_node->line + 1),
-                    biggest_size_in_bits,
-                    biggest_align_in_bits,
-                    union_offset_in_bits,
-                    0, union_di_type);
-
-            // create debug type for root struct
-            ZigLLVMDIType *di_root_members[2];
-            di_root_members[enum_type->data.enumeration.gen_tag_index] = tag_member_di_type;
-            di_root_members[enum_type->data.enumeration.gen_union_index] = union_member_di_type;
-
-            uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, enum_type->type_ref);
-            uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, enum_type->type_ref);
-            ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
-                    ZigLLVMFileToScope(import->di_file),
-                    buf_ptr(&enum_type->name),
-                    import->di_file, (unsigned)(decl_node->line + 1),
-                    debug_size_in_bits,
-                    debug_align_in_bits,
-                    0, nullptr, di_root_members, 2, 0, nullptr, "");
-
-            ZigLLVMReplaceTemporary(g->dbuilder, enum_type->di_type, replacement_di_type);
-            enum_type->di_type = replacement_di_type;
+        ZigLLVMReplaceTemporary(g->dbuilder, enum_type->di_type, replacement_di_type);
+        enum_type->di_type = replacement_di_type;
+        return;
+    }
+
+    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;
+
+    uint64_t align_of_tag_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_int_type->type_ref);
+
+    if (most_aligned_union_member) {
+        // create llvm type for union
+        uint64_t padding_in_bits = biggest_size_in_bits - size_of_most_aligned_member_in_bits;
+        LLVMTypeRef union_type_ref;
+        if (padding_in_bits > 0) {
+            TypeTableEntry *u8_type = get_int_type(g, false, 8);
+            TypeTableEntry *padding_array = get_array_type(g, u8_type, padding_in_bits / 8);
+            LLVMTypeRef union_element_types[] = {
+                most_aligned_union_member->type_ref,
+                padding_array->type_ref,
+            };
+            union_type_ref = LLVMStructType(union_element_types, 2, false);
         } else {
-            // create llvm type for root struct
-            enum_type->type_ref = tag_type_entry->type_ref;
-
-            // create debug type for tag
-            uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type_entry->type_ref);
-            uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_type_entry->type_ref);
-            ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder,
-                    ZigLLVMFileToScope(import->di_file), buf_ptr(&enum_type->name),
-                    import->di_file, (unsigned)(decl_node->line + 1),
-                    tag_debug_size_in_bits,
-                    tag_debug_align_in_bits,
-                    di_enumerators, field_count,
-                    tag_type_entry->di_type, "");
+            union_type_ref = most_aligned_union_member->type_ref;
+        }
+        enum_type->data.enumeration.union_type_ref = union_type_ref;
 
-            ZigLLVMReplaceTemporary(g->dbuilder, enum_type->di_type, tag_di_type);
-            enum_type->di_type = tag_di_type;
+        assert(8*LLVMABIAlignmentOfType(g->target_data_ref, union_type_ref) >= biggest_align_in_bits);
+        assert(8*LLVMStoreSizeOfType(g->target_data_ref, union_type_ref) >= biggest_size_in_bits);
+
+        if (align_of_tag_in_bits >= biggest_align_in_bits) {
+            enum_type->data.enumeration.gen_tag_index = 0;
+            enum_type->data.enumeration.gen_union_index = 1;
+        } else {
+            enum_type->data.enumeration.gen_union_index = 0;
+            enum_type->data.enumeration.gen_tag_index = 1;
         }
+
+        // create llvm type for root struct
+        LLVMTypeRef root_struct_element_types[2];
+        root_struct_element_types[enum_type->data.enumeration.gen_tag_index] = tag_type_entry->type_ref;
+        root_struct_element_types[enum_type->data.enumeration.gen_union_index] = union_type_ref;
+        LLVMStructSetBody(enum_type->type_ref, root_struct_element_types, 2, false);
+
+        // create debug type for tag
+        uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type_entry->type_ref);
+        uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_type_entry->type_ref);
+        ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder,
+                ZigLLVMTypeToScope(enum_type->di_type), "AnonEnum",
+                import->di_file, (unsigned)(decl_node->line + 1),
+                tag_debug_size_in_bits, tag_debug_align_in_bits, di_enumerators, field_count,
+                tag_type_entry->di_type, "");
+
+        // create debug type for union
+        ZigLLVMDIType *union_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder,
+                ZigLLVMTypeToScope(enum_type->di_type), "AnonUnion",
+                import->di_file, (unsigned)(decl_node->line + 1),
+                biggest_size_in_bits, biggest_align_in_bits, 0, union_inner_di_types,
+                gen_field_count, 0, "");
+
+        // create debug types for members of root struct
+        uint64_t tag_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, enum_type->type_ref,
+                enum_type->data.enumeration.gen_tag_index);
+        ZigLLVMDIType *tag_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder,
+                ZigLLVMTypeToScope(enum_type->di_type), "tag_field",
+                import->di_file, (unsigned)(decl_node->line + 1),
+                tag_debug_size_in_bits,
+                tag_debug_align_in_bits,
+                tag_offset_in_bits,
+                0, tag_di_type);
+
+        uint64_t union_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, enum_type->type_ref,
+                enum_type->data.enumeration.gen_union_index);
+        ZigLLVMDIType *union_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder,
+                ZigLLVMTypeToScope(enum_type->di_type), "union_field",
+                import->di_file, (unsigned)(decl_node->line + 1),
+                biggest_size_in_bits,
+                biggest_align_in_bits,
+                union_offset_in_bits,
+                0, union_di_type);
+
+        // create debug type for root struct
+        ZigLLVMDIType *di_root_members[2];
+        di_root_members[enum_type->data.enumeration.gen_tag_index] = tag_member_di_type;
+        di_root_members[enum_type->data.enumeration.gen_union_index] = union_member_di_type;
+
+        uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, enum_type->type_ref);
+        uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, enum_type->type_ref);
+        ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
+                ZigLLVMFileToScope(import->di_file),
+                buf_ptr(&enum_type->name),
+                import->di_file, (unsigned)(decl_node->line + 1),
+                debug_size_in_bits,
+                debug_align_in_bits,
+                0, nullptr, di_root_members, 2, 0, nullptr, "");
+
+        ZigLLVMReplaceTemporary(g->dbuilder, enum_type->di_type, replacement_di_type);
+        enum_type->di_type = replacement_di_type;
+    } else {
+        // create llvm type for root struct
+        enum_type->type_ref = tag_type_entry->type_ref;
+
+        // create debug type for tag
+        uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type_entry->type_ref);
+        uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_type_entry->type_ref);
+        ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder,
+                ZigLLVMFileToScope(import->di_file), buf_ptr(&enum_type->name),
+                import->di_file, (unsigned)(decl_node->line + 1),
+                tag_debug_size_in_bits,
+                tag_debug_align_in_bits,
+                di_enumerators, field_count,
+                tag_type_entry->di_type, "");
+
+        ZigLLVMReplaceTemporary(g->dbuilder, enum_type->di_type, tag_di_type);
+        enum_type->di_type = tag_di_type;
     }
 }
 
@@ -1875,9 +1896,11 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) {
     enum_type->data.enumeration.zero_bits_known = true;
 
     // also compute abi_alignment
-    TypeTableEntry *tag_int_type = get_smallest_unsigned_int_type(g, field_count);
-    uint32_t align_of_tag_in_bytes = LLVMABIAlignmentOfType(g->target_data_ref, tag_int_type->type_ref);
-    enum_type->data.enumeration.abi_alignment = max(align_of_tag_in_bytes, biggest_align_bytes);
+    if (!enum_type->zero_bits) {
+        TypeTableEntry *tag_int_type = get_smallest_unsigned_int_type(g, field_count);
+        uint32_t align_of_tag_in_bytes = LLVMABIAlignmentOfType(g->target_data_ref, tag_int_type->type_ref);
+        enum_type->data.enumeration.abi_alignment = max(align_of_tag_in_bytes, biggest_align_bytes);
+    }
 }
 
 static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {
src/codegen.cpp
@@ -3384,6 +3384,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
         case IrInstructionIdEmbedFile:
         case IrInstructionIdIntType:
         case IrInstructionIdMemberCount:
+        case IrInstructionIdMemberType:
+        case IrInstructionIdMemberName:
         case IrInstructionIdAlignOf:
         case IrInstructionIdFnProto:
         case IrInstructionIdTestComptime:
@@ -4867,6 +4869,8 @@ static void define_builtin_fns(CodeGen *g) {
     create_builtin_fn(g, BuiltinFnIdMaxValue, "maxValue", 1);
     create_builtin_fn(g, BuiltinFnIdMinValue, "minValue", 1);
     create_builtin_fn(g, BuiltinFnIdMemberCount, "memberCount", 1);
+    create_builtin_fn(g, BuiltinFnIdMemberType, "memberType", 2);
+    create_builtin_fn(g, BuiltinFnIdMemberName, "memberName", 2);
     create_builtin_fn(g, BuiltinFnIdTypeof, "typeOf", 1); // TODO rename to TypeOf
     create_builtin_fn(g, BuiltinFnIdAddWithOverflow, "addWithOverflow", 4);
     create_builtin_fn(g, BuiltinFnIdSubWithOverflow, "subWithOverflow", 4);
src/ir.cpp
@@ -411,6 +411,14 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionMemberCount *) {
     return IrInstructionIdMemberCount;
 }
 
+static constexpr IrInstructionId ir_instruction_id(IrInstructionMemberType *) {
+    return IrInstructionIdMemberType;
+}
+
+static constexpr IrInstructionId ir_instruction_id(IrInstructionMemberName *) {
+    return IrInstructionIdMemberName;
+}
+
 static constexpr IrInstructionId ir_instruction_id(IrInstructionBreakpoint *) {
     return IrInstructionIdBreakpoint;
 }
@@ -1783,6 +1791,32 @@ static IrInstruction *ir_build_member_count(IrBuilder *irb, Scope *scope, AstNod
     return &instruction->base;
 }
 
+static IrInstruction *ir_build_member_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
+        IrInstruction *container_type, IrInstruction *member_index)
+{
+    IrInstructionMemberType *instruction = ir_build_instruction<IrInstructionMemberType>(irb, scope, source_node);
+    instruction->container_type = container_type;
+    instruction->member_index = member_index;
+
+    ir_ref_instruction(container_type, irb->current_basic_block);
+    ir_ref_instruction(member_index, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstruction *ir_build_member_name(IrBuilder *irb, Scope *scope, AstNode *source_node,
+        IrInstruction *container_type, IrInstruction *member_index)
+{
+    IrInstructionMemberName *instruction = ir_build_instruction<IrInstructionMemberName>(irb, scope, source_node);
+    instruction->container_type = container_type;
+    instruction->member_index = member_index;
+
+    ir_ref_instruction(container_type, irb->current_basic_block);
+    ir_ref_instruction(member_index, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
 static IrInstruction *ir_build_breakpoint(IrBuilder *irb, Scope *scope, AstNode *source_node) {
     IrInstructionBreakpoint *instruction = ir_build_instruction<IrInstructionBreakpoint>(irb, scope, source_node);
     return &instruction->base;
@@ -2727,6 +2761,22 @@ static IrInstruction *ir_instruction_membercount_get_dep(IrInstructionMemberCoun
     }
 }
 
+static IrInstruction *ir_instruction_membertype_get_dep(IrInstructionMemberType *instruction, size_t index) {
+    switch (index) {
+        case 0: return instruction->container_type;
+        case 1: return instruction->member_index;
+        default: return nullptr;
+    }
+}
+
+static IrInstruction *ir_instruction_membername_get_dep(IrInstructionMemberName *instruction, size_t index) {
+    switch (index) {
+        case 0: return instruction->container_type;
+        case 1: return instruction->member_index;
+        default: return nullptr;
+    }
+}
+
 static IrInstruction *ir_instruction_breakpoint_get_dep(IrInstructionBreakpoint *instruction, size_t index) {
     return nullptr;
 }
@@ -3143,6 +3193,10 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t
             return ir_instruction_slice_get_dep((IrInstructionSlice *) instruction, index);
         case IrInstructionIdMemberCount:
             return ir_instruction_membercount_get_dep((IrInstructionMemberCount *) instruction, index);
+        case IrInstructionIdMemberType:
+            return ir_instruction_membertype_get_dep((IrInstructionMemberType *) instruction, index);
+        case IrInstructionIdMemberName:
+            return ir_instruction_membername_get_dep((IrInstructionMemberName *) instruction, index);
         case IrInstructionIdBreakpoint:
             return ir_instruction_breakpoint_get_dep((IrInstructionBreakpoint *) instruction, index);
         case IrInstructionIdReturnAddress:
@@ -4379,6 +4433,36 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
 
                 return ir_build_member_count(irb, scope, node, arg0_value);
             }
+        case BuiltinFnIdMemberType:
+            {
+                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;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_instruction)
+                    return arg1_value;
+
+
+                return ir_build_member_type(irb, scope, node, arg0_value, arg1_value);
+            }
+        case BuiltinFnIdMemberName:
+            {
+                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;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_instruction)
+                    return arg1_value;
+
+
+                return ir_build_member_name(irb, scope, node, arg0_value, arg1_value);
+            }
         case BuiltinFnIdBreakpoint:
             return ir_build_breakpoint(irb, scope, node);
         case BuiltinFnIdReturnAddress:
@@ -14337,6 +14421,90 @@ static TypeTableEntry *ir_analyze_instruction_member_count(IrAnalyze *ira, IrIns
     return ira->codegen->builtin_types.entry_num_lit_int;
 }
 
+static TypeTableEntry *ir_analyze_instruction_member_type(IrAnalyze *ira, IrInstructionMemberType *instruction) {
+    IrInstruction *container_type_value = instruction->container_type->other;
+    TypeTableEntry *container_type = ir_resolve_type(ira, container_type_value);
+    if (type_is_invalid(container_type))
+        return ira->codegen->builtin_types.entry_invalid;
+
+    uint64_t member_index;
+    IrInstruction *index_value = instruction->member_index->other;
+    if (!ir_resolve_usize(ira, index_value, &member_index))
+        return ira->codegen->builtin_types.entry_invalid;
+
+    if (container_type->id == TypeTableEntryIdStruct) {
+        if (member_index >= container_type->data.structure.src_field_count) {
+            ir_add_error(ira, index_value,
+                buf_sprintf("member index %" ZIG_PRI_u64 " out of bounds; '%s' has %" PRIu32 " members",
+                    member_index, buf_ptr(&container_type->name), container_type->data.structure.src_field_count));
+            return ira->codegen->builtin_types.entry_invalid;
+        }
+        TypeStructField *field = &container_type->data.structure.fields[member_index];
+
+        ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
+        out_val->data.x_type = field->type_entry;
+        return ira->codegen->builtin_types.entry_type;
+    } else if (container_type->id == TypeTableEntryIdEnum) {
+        if (member_index >= container_type->data.enumeration.src_field_count) {
+            ir_add_error(ira, index_value,
+                buf_sprintf("member index %" ZIG_PRI_u64 " out of bounds; '%s' has %" PRIu32 " members",
+                    member_index, buf_ptr(&container_type->name), container_type->data.enumeration.src_field_count));
+            return ira->codegen->builtin_types.entry_invalid;
+        }
+        TypeEnumField *field = &container_type->data.enumeration.fields[member_index];
+
+        ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
+        out_val->data.x_type = field->type_entry;
+        return ira->codegen->builtin_types.entry_type;
+    } else {
+        ir_add_error(ira, container_type_value,
+            buf_sprintf("type '%s' does not support @memberType", buf_ptr(&container_type->name)));
+        return ira->codegen->builtin_types.entry_invalid;
+    }
+}
+
+static TypeTableEntry *ir_analyze_instruction_member_name(IrAnalyze *ira, IrInstructionMemberName *instruction) {
+    IrInstruction *container_type_value = instruction->container_type->other;
+    TypeTableEntry *container_type = ir_resolve_type(ira, container_type_value);
+    if (type_is_invalid(container_type))
+        return ira->codegen->builtin_types.entry_invalid;
+
+    uint64_t member_index;
+    IrInstruction *index_value = instruction->member_index->other;
+    if (!ir_resolve_usize(ira, index_value, &member_index))
+        return ira->codegen->builtin_types.entry_invalid;
+
+    if (container_type->id == TypeTableEntryIdStruct) {
+        if (member_index >= container_type->data.structure.src_field_count) {
+            ir_add_error(ira, index_value,
+                buf_sprintf("member index %" ZIG_PRI_u64 " out of bounds; '%s' has %" PRIu32 " members",
+                    member_index, buf_ptr(&container_type->name), container_type->data.structure.src_field_count));
+            return ira->codegen->builtin_types.entry_invalid;
+        }
+        TypeStructField *field = &container_type->data.structure.fields[member_index];
+
+        ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
+        init_const_str_lit(ira->codegen, out_val, field->name);
+        return out_val->type;
+    } else if (container_type->id == TypeTableEntryIdEnum) {
+        if (member_index >= container_type->data.enumeration.src_field_count) {
+            ir_add_error(ira, index_value,
+                buf_sprintf("member index %" ZIG_PRI_u64 " out of bounds; '%s' has %" PRIu32 " members",
+                    member_index, buf_ptr(&container_type->name), container_type->data.enumeration.src_field_count));
+            return ira->codegen->builtin_types.entry_invalid;
+        }
+        TypeEnumField *field = &container_type->data.enumeration.fields[member_index];
+
+        ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
+        init_const_str_lit(ira->codegen, out_val, field->name);
+        return out_val->type;
+    } else {
+        ir_add_error(ira, container_type_value,
+            buf_sprintf("type '%s' does not support @memberName", buf_ptr(&container_type->name)));
+        return ira->codegen->builtin_types.entry_invalid;
+    }
+}
+
 static TypeTableEntry *ir_analyze_instruction_breakpoint(IrAnalyze *ira, IrInstructionBreakpoint *instruction) {
     ir_build_breakpoint_from(&ira->new_irb, &instruction->base);
     return ira->codegen->builtin_types.entry_void;
@@ -15606,6 +15774,10 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
             return ir_analyze_instruction_slice(ira, (IrInstructionSlice *)instruction);
         case IrInstructionIdMemberCount:
             return ir_analyze_instruction_member_count(ira, (IrInstructionMemberCount *)instruction);
+        case IrInstructionIdMemberType:
+            return ir_analyze_instruction_member_type(ira, (IrInstructionMemberType *)instruction);
+        case IrInstructionIdMemberName:
+            return ir_analyze_instruction_member_name(ira, (IrInstructionMemberName *)instruction);
         case IrInstructionIdBreakpoint:
             return ir_analyze_instruction_breakpoint(ira, (IrInstructionBreakpoint *)instruction);
         case IrInstructionIdReturnAddress:
@@ -15815,6 +15987,8 @@ bool ir_has_side_effects(IrInstruction *instruction) {
         case IrInstructionIdBoolNot:
         case IrInstructionIdSlice:
         case IrInstructionIdMemberCount:
+        case IrInstructionIdMemberType:
+        case IrInstructionIdMemberName:
         case IrInstructionIdAlignOf:
         case IrInstructionIdReturnAddress:
         case IrInstructionIdFrameAddress:
src/ir_print.cpp
@@ -658,6 +658,22 @@ static void ir_print_member_count(IrPrint *irp, IrInstructionMemberCount *instru
     fprintf(irp->f, ")");
 }
 
+static void ir_print_member_type(IrPrint *irp, IrInstructionMemberType *instruction) {
+    fprintf(irp->f, "@memberType(");
+    ir_print_other_instruction(irp, instruction->container_type);
+    fprintf(irp->f, ", ");
+    ir_print_other_instruction(irp, instruction->member_index);
+    fprintf(irp->f, ")");
+}
+
+static void ir_print_member_name(IrPrint *irp, IrInstructionMemberName *instruction) {
+    fprintf(irp->f, "@memberName(");
+    ir_print_other_instruction(irp, instruction->container_type);
+    fprintf(irp->f, ", ");
+    ir_print_other_instruction(irp, instruction->member_index);
+    fprintf(irp->f, ")");
+}
+
 static void ir_print_breakpoint(IrPrint *irp, IrInstructionBreakpoint *instruction) {
     fprintf(irp->f, "@breakpoint()");
 }
@@ -1148,6 +1164,12 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
         case IrInstructionIdMemberCount:
             ir_print_member_count(irp, (IrInstructionMemberCount *)instruction);
             break;
+        case IrInstructionIdMemberType:
+            ir_print_member_type(irp, (IrInstructionMemberType *)instruction);
+            break;
+        case IrInstructionIdMemberName:
+            ir_print_member_name(irp, (IrInstructionMemberName *)instruction);
+            break;
         case IrInstructionIdBreakpoint:
             ir_print_breakpoint(irp, (IrInstructionBreakpoint *)instruction);
             break;
src-self-hosted/main.zig
@@ -5,7 +5,6 @@ const heap = @import("std").mem;
 
 // TODO: sync up CLI with c++ code
 // TODO: concurrency
-// TODO: ability to iterate over enums at compile time (for listing targets)
 
 error InvalidArgument;
 error MissingArg0;
test/cases/reflection.zig
@@ -25,3 +25,46 @@ test "reflection: function return type, var args, and param types" {
 fn dummy(a: bool, b: i32, c: f32) -> i32 { 1234 }
 fn dummy_varargs(args: ...) {}
 
+test "reflection: struct member types and names" {
+    comptime {
+        assert(@memberCount(Foo) == 3);
+
+        assert(@memberType(Foo, 0) == i32);
+        assert(@memberType(Foo, 1) == bool);
+        assert(@memberType(Foo, 2) == void);
+
+        assert(mem.eql(u8, @memberName(Foo, 0), "one"));
+        assert(mem.eql(u8, @memberName(Foo, 1), "two"));
+        assert(mem.eql(u8, @memberName(Foo, 2), "three"));
+    }
+}
+
+test "reflection: enum member types and names" {
+    comptime {
+        assert(@memberCount(Bar) == 4);
+
+        assert(@memberType(Bar, 0) == void);
+        assert(@memberType(Bar, 1) == i32);
+        assert(@memberType(Bar, 2) == bool);
+        assert(@memberType(Bar, 3) == f64);
+
+        assert(mem.eql(u8, @memberName(Bar, 0), "One"));
+        assert(mem.eql(u8, @memberName(Bar, 1), "Two"));
+        assert(mem.eql(u8, @memberName(Bar, 2), "Three"));
+        assert(mem.eql(u8, @memberName(Bar, 3), "Four"));
+    }
+
+}
+
+const Foo = struct {
+    one: i32,
+    two: bool,
+    three: void,
+};
+
+const Bar = enum {
+    One,
+    Two: i32,
+    Three: bool,
+    Four: f64,
+};
test/compile_errors.zig
@@ -2289,4 +2289,50 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
         \\fn add(a: i32, b: i32) -> i32 { return a + b; }
     ,
         ".tmp_source.zig:2:32: error: arg index 2 out of bounds; 'fn(i32, i32) -> i32' has 2 arguments");
+
+    cases.add("@memberType on unsupported type",
+        \\comptime {
+        \\    _ = @memberType(i32, 0);
+        \\}
+    ,
+        ".tmp_source.zig:2:21: error: type 'i32' does not support @memberType");
+
+    cases.add("@memberType struct out of bounds",
+        \\comptime {
+        \\    _ = @memberType(Foo, 0);
+        \\}
+        \\const Foo = struct {};
+    ,
+        ".tmp_source.zig:2:26: error: member index 0 out of bounds; 'Foo' has 0 members");
+
+    cases.add("@memberType enum out of bounds",
+        \\comptime {
+        \\    _ = @memberType(Foo, 0);
+        \\}
+        \\const Foo = enum {};
+    ,
+        ".tmp_source.zig:2:26: error: member index 0 out of bounds; 'Foo' has 0 members");
+
+    cases.add("@memberName on unsupported type",
+        \\comptime {
+        \\    _ = @memberName(i32, 0);
+        \\}
+    ,
+        ".tmp_source.zig:2:21: error: type 'i32' does not support @memberName");
+
+    cases.add("@memberName struct out of bounds",
+        \\comptime {
+        \\    _ = @memberName(Foo, 0);
+        \\}
+        \\const Foo = struct {};
+    ,
+        ".tmp_source.zig:2:26: error: member index 0 out of bounds; 'Foo' has 0 members");
+
+    cases.add("@memberName enum out of bounds",
+        \\comptime {
+        \\    _ = @memberName(Foo, 0);
+        \\}
+        \\const Foo = enum {};
+    ,
+        ".tmp_source.zig:2:26: error: member index 0 out of bounds; 'Foo' has 0 members");
 }