mirror of
https://github.com/ziglang/zig.git
synced 2026-02-21 16:54:52 +00:00
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:
parent
f0dafd3f20
commit
634e8713c3
@ -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;
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
239
src/analyze.cpp
239
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;
|
||||
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) {
|
||||
|
||||
@ -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);
|
||||
|
||||
174
src/ir.cpp
174
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:
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
@ -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");
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user