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
This commit is contained in:
Andrew Kelley 2017-11-06 22:07:19 -05:00
parent f0dafd3f20
commit 634e8713c3
8 changed files with 438 additions and 109 deletions

View File

@ -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;

View File

@ -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;
};

View File

@ -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;
if (enum_type->data.enumeration.is_invalid)
return;
uint64_t align_of_tag_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_int_type->type_ref);
if (enum_type->zero_bits) {
enum_type->type_ref = LLVMVoidType();
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;
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, "");
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);
ZigLLVMReplaceTemporary(g->dbuilder, enum_type->di_type, replacement_di_type);
enum_type->di_type = replacement_di_type;
return;
}
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;
}
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;
// 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);
uint64_t align_of_tag_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_int_type->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,
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;
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, "");
ZigLLVMReplaceTemporary(g->dbuilder, enum_type->di_type, tag_di_type);
enum_type->di_type = tag_di_type;
union_type_ref = most_aligned_union_member->type_ref;
}
enum_type->data.enumeration.union_type_ref = union_type_ref;
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) {

View File

@ -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);

View File

@ -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:

View File

@ -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;

View File

@ -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,
};

View File

@ -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");
}