Commit 634e8713c3
Changed files (8)
src-self-hosted
test
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");
}