From a31b23c46ba2a8c28df01adc1aa0b4d878b9a5cf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 3 Nov 2017 00:00:57 -0400 Subject: more compile-time type reflection See #383 --- src/ir.cpp | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) (limited to 'src/ir.cpp') diff --git a/src/ir.cpp b/src/ir.cpp index 6c6ce676f6..e2e61fed7d 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -11643,6 +11643,55 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru buf_ptr(&child_type->name), buf_ptr(field_name))); return ira->codegen->builtin_types.entry_invalid; } + } else if (child_type->id == TypeTableEntryIdErrorUnion) { + if (buf_eql_str(field_name, "Child")) { + bool ptr_is_const = true; + bool ptr_is_volatile = false; + return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, + create_const_type(ira->codegen, child_type->data.error.child_type), + ira->codegen->builtin_types.entry_type, + ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); + } else { + ir_add_error(ira, &field_ptr_instruction->base, + buf_sprintf("type '%s' has no member called '%s'", + buf_ptr(&child_type->name), buf_ptr(field_name))); + return ira->codegen->builtin_types.entry_invalid; + } + } else if (child_type->id == TypeTableEntryIdMaybe) { + if (buf_eql_str(field_name, "Child")) { + bool ptr_is_const = true; + bool ptr_is_volatile = false; + return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, + create_const_type(ira->codegen, child_type->data.maybe.child_type), + ira->codegen->builtin_types.entry_type, + ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); + } else { + ir_add_error(ira, &field_ptr_instruction->base, + buf_sprintf("type '%s' has no member called '%s'", + buf_ptr(&child_type->name), buf_ptr(field_name))); + return ira->codegen->builtin_types.entry_invalid; + } + } else if (child_type->id == TypeTableEntryIdFn) { + if (buf_eql_str(field_name, "ReturnType")) { + bool ptr_is_const = true; + bool ptr_is_volatile = false; + return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, + create_const_type(ira->codegen, child_type->data.fn.fn_type_id.return_type), + ira->codegen->builtin_types.entry_type, + ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); + } else if (buf_eql_str(field_name, "is_var_args")) { + bool ptr_is_const = true; + bool ptr_is_volatile = false; + return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, + create_const_bool(ira->codegen, child_type->data.fn.fn_type_id.is_var_args), + ira->codegen->builtin_types.entry_bool, + ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); + } else { + ir_add_error(ira, &field_ptr_instruction->base, + buf_sprintf("type '%s' has no member called '%s'", + buf_ptr(&child_type->name), buf_ptr(field_name))); + return ira->codegen->builtin_types.entry_invalid; + } } else { ir_add_error(ira, &field_ptr_instruction->base, buf_sprintf("type '%s' does not support field access", buf_ptr(&child_type->name))); -- cgit v1.2.3 From f0d755153d90f35da612c8aa6f1853f5112f5cf1 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 4 Nov 2017 16:19:43 -0400 Subject: add compile-time reflection for function arg types See #383 --- src/all_types.hpp | 9 ++++++ src/codegen.cpp | 4 ++- src/ir.cpp | 80 +++++++++++++++++++++++++++++++++++++++++++++++ src/ir_print.cpp | 12 +++++++ test/cases/reflection.zig | 9 ++++-- test/compile_errors.zig | 14 +++++++++ 6 files changed, 125 insertions(+), 3 deletions(-) (limited to 'src/ir.cpp') diff --git a/src/all_types.hpp b/src/all_types.hpp index af885413f7..7f06134c2c 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1261,6 +1261,7 @@ enum BuiltinFnId { BuiltinFnIdAlignCast, BuiltinFnIdOpaqueType, BuiltinFnIdSetAlignStack, + BuiltinFnIdArgType, }; struct BuiltinFnEntry { @@ -1882,6 +1883,7 @@ enum IrInstructionId { IrInstructionIdAlignCast, IrInstructionIdOpaqueType, IrInstructionIdSetAlignStack, + IrInstructionIdArgType, }; struct IrInstruction { @@ -2682,6 +2684,13 @@ struct IrInstructionSetAlignStack { IrInstruction *align_bytes; }; +struct IrInstructionArgType { + IrInstruction base; + + IrInstruction *fn_type; + IrInstruction *arg_index; +}; + static const size_t slice_ptr_index = 0; static const size_t slice_len_index = 1; diff --git a/src/codegen.cpp b/src/codegen.cpp index 4a69f40da1..2a2cd3ba10 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3401,6 +3401,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdPtrTypeOf: case IrInstructionIdOpaqueType: case IrInstructionIdSetAlignStack: + case IrInstructionIdArgType: zig_unreachable(); case IrInstructionIdReturn: return ir_render_return(g, executable, (IrInstructionReturn *)instruction); @@ -4866,7 +4867,7 @@ 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, BuiltinFnIdTypeof, "typeOf", 1); + 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); create_builtin_fn(g, BuiltinFnIdMulWithOverflow, "mulWithOverflow", 4); @@ -4913,6 +4914,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdAlignCast, "alignCast", 2); create_builtin_fn(g, BuiltinFnIdOpaqueType, "OpaqueType", 0); create_builtin_fn(g, BuiltinFnIdSetAlignStack, "setAlignStack", 1); + create_builtin_fn(g, BuiltinFnIdArgType, "ArgType", 2); } static const char *bool_to_str(bool b) { diff --git a/src/ir.cpp b/src/ir.cpp index e2e61fed7d..49e043a837 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -567,6 +567,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSetAlignStack *) return IrInstructionIdSetAlignStack; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionArgType *) { + return IrInstructionIdArgType; +} + template static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { T *special_instruction = allocate(1); @@ -2263,6 +2267,19 @@ static IrInstruction *ir_build_set_align_stack(IrBuilder *irb, Scope *scope, Ast return &instruction->base; } +static IrInstruction *ir_build_arg_type(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *fn_type, IrInstruction *arg_index) +{ + IrInstructionArgType *instruction = ir_build_instruction(irb, scope, source_node); + instruction->fn_type = fn_type; + instruction->arg_index = arg_index; + + ir_ref_instruction(fn_type, irb->current_basic_block); + ir_ref_instruction(arg_index, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_instruction_br_get_dep(IrInstructionBr *instruction, size_t index) { return nullptr; } @@ -2992,6 +3009,14 @@ static IrInstruction *ir_instruction_setalignstack_get_dep(IrInstructionSetAlign } } +static IrInstruction *ir_instruction_argtype_get_dep(IrInstructionArgType *instruction, size_t index) { + switch (index) { + case 0: return instruction->fn_type; + case 1: return instruction->arg_index; + default: return nullptr; + } +} + static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t index) { switch (instruction->id) { case IrInstructionIdInvalid: @@ -3194,6 +3219,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t return ir_instruction_opaquetype_get_dep((IrInstructionOpaqueType *) instruction, index); case IrInstructionIdSetAlignStack: return ir_instruction_setalignstack_get_dep((IrInstructionSetAlignStack *) instruction, index); + case IrInstructionIdArgType: + return ir_instruction_argtype_get_dep((IrInstructionArgType *) instruction, index); } zig_unreachable(); } @@ -4629,6 +4656,20 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return ir_build_set_align_stack(irb, scope, node, arg0_value); } + case BuiltinFnIdArgType: + { + 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_arg_type(irb, scope, node, arg0_value, arg1_value); + } } zig_unreachable(); } @@ -11686,6 +11727,13 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru create_const_bool(ira->codegen, child_type->data.fn.fn_type_id.is_var_args), ira->codegen->builtin_types.entry_bool, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); + } else if (buf_eql_str(field_name, "arg_count")) { + bool ptr_is_const = true; + bool ptr_is_volatile = false; + return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, + create_const_usize(ira->codegen, child_type->data.fn.fn_type_id.param_count), + ira->codegen->builtin_types.entry_usize, + ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); } else { ir_add_error(ira, &field_ptr_instruction->base, buf_sprintf("type '%s' has no member called '%s'", @@ -15395,6 +15443,35 @@ static TypeTableEntry *ir_analyze_instruction_set_align_stack(IrAnalyze *ira, Ir return ira->codegen->builtin_types.entry_void; } +static TypeTableEntry *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstructionArgType *instruction) { + IrInstruction *fn_type_inst = instruction->fn_type->other; + TypeTableEntry *fn_type = ir_resolve_type(ira, fn_type_inst); + if (type_is_invalid(fn_type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *arg_index_inst = instruction->arg_index->other; + uint64_t arg_index; + if (!ir_resolve_usize(ira, arg_index_inst, &arg_index)) + return ira->codegen->builtin_types.entry_invalid; + + if (fn_type->id != TypeTableEntryIdFn) { + ir_add_error(ira, fn_type_inst, buf_sprintf("expected function, found '%s'", buf_ptr(&fn_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; + if (arg_index >= fn_type_id->param_count) { + ir_add_error(ira, arg_index_inst, + buf_sprintf("arg index %" ZIG_PRI_usize " out of bounds; '%s' has %" ZIG_PRI_usize " arguments", + arg_index, buf_ptr(&fn_type->name), fn_type_id->param_count)); + return ira->codegen->builtin_types.entry_invalid; + } + + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + out_val->data.x_type = fn_type_id->param_info[arg_index].type; + return ira->codegen->builtin_types.entry_type; +} + static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: @@ -15585,6 +15662,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_opaque_type(ira, (IrInstructionOpaqueType *)instruction); case IrInstructionIdSetAlignStack: return ir_analyze_instruction_set_align_stack(ira, (IrInstructionSetAlignStack *)instruction); + case IrInstructionIdArgType: + return ir_analyze_instruction_arg_type(ira, (IrInstructionArgType *)instruction); } zig_unreachable(); } @@ -15765,6 +15844,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdTypeId: case IrInstructionIdAlignCast: case IrInstructionIdOpaqueType: + case IrInstructionIdArgType: return false; case IrInstructionIdAsm: { diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 63ccd76ef0..c0f3169b3e 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -954,6 +954,15 @@ static void ir_print_set_align_stack(IrPrint *irp, IrInstructionSetAlignStack *i fprintf(irp->f, ")"); } +static void ir_print_arg_type(IrPrint *irp, IrInstructionArgType *instruction) { + fprintf(irp->f, "@ArgType("); + ir_print_other_instruction(irp, instruction->fn_type); + fprintf(irp->f, ","); + ir_print_other_instruction(irp, instruction->arg_index); + fprintf(irp->f, ")"); +} + + static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_prefix(irp, instruction); switch (instruction->id) { @@ -1256,6 +1265,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdSetAlignStack: ir_print_set_align_stack(irp, (IrInstructionSetAlignStack *)instruction); break; + case IrInstructionIdArgType: + ir_print_arg_type(irp, (IrInstructionArgType *)instruction); + break; } fprintf(irp->f, "\n"); } diff --git a/test/cases/reflection.zig b/test/cases/reflection.zig index 33bad839fb..13adfd20cd 100644 --- a/test/cases/reflection.zig +++ b/test/cases/reflection.zig @@ -10,13 +10,18 @@ test "reflection: array, pointer, nullable, error union type child" { } } -test "reflection: function return type and var args" { +test "reflection: function return type, var args, and param types" { comptime { assert(@typeOf(dummy).ReturnType == i32); assert(!@typeOf(dummy).is_var_args); assert(@typeOf(dummy_varargs).is_var_args); + assert(@typeOf(dummy).arg_count == 3); + assert(@ArgType(@typeOf(dummy), 0) == bool); + assert(@ArgType(@typeOf(dummy), 1) == i32); + assert(@ArgType(@typeOf(dummy), 2) == f32); } } -fn dummy() -> i32 { 1234 } +fn dummy(a: bool, b: i32, c: f32) -> i32 { 1234 } fn dummy_varargs(args: ...) {} + diff --git a/test/compile_errors.zig b/test/compile_errors.zig index f8e08d599f..6901bd302c 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2275,4 +2275,18 @@ pub fn addCases(cases: &tests.CompileErrorContext) { , ".tmp_source.zig:2:1: error: invalid character: '\\t'"); + cases.add("@ArgType given non function parameter", + \\comptime { + \\ _ = @ArgType(i32, 3); + \\} + , + ".tmp_source.zig:2:18: error: expected function, found 'i32'"); + + cases.add("@ArgType arg index out of bounds", + \\comptime { + \\ _ = @ArgType(@typeOf(add), 2); + \\} + \\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"); } -- cgit v1.2.3 From 4cc9fe90a8a3c0bce803bf9fffd66477da9e37d0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 4 Nov 2017 16:40:55 -0400 Subject: fix build on MacOS --- src/ir.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/ir.cpp') diff --git a/src/ir.cpp b/src/ir.cpp index 49e043a837..556f255a0a 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15462,7 +15462,7 @@ static TypeTableEntry *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstruc FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; if (arg_index >= fn_type_id->param_count) { ir_add_error(ira, arg_index_inst, - buf_sprintf("arg index %" ZIG_PRI_usize " out of bounds; '%s' has %" ZIG_PRI_usize " arguments", + buf_sprintf("arg index %" ZIG_PRI_u64 " out of bounds; '%s' has %" ZIG_PRI_usize " arguments", arg_index, buf_ptr(&fn_type->name), fn_type_id->param_count)); return ira->codegen->builtin_types.entry_invalid; } -- cgit v1.2.3 From 634e8713c394bacfe080d03256d1dd4f9a43dd8c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 6 Nov 2017 22:07:19 -0500 Subject: 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 --- src-self-hosted/main.zig | 1 - src/all_types.hpp | 18 ++++ src/analyze.cpp | 243 +++++++++++++++++++++++++--------------------- src/codegen.cpp | 4 + src/ir.cpp | 174 +++++++++++++++++++++++++++++++++ src/ir_print.cpp | 22 +++++ test/cases/reflection.zig | 43 ++++++++ test/compile_errors.zig | 46 +++++++++ 8 files changed, 440 insertions(+), 111 deletions(-) (limited to 'src/ir.cpp') diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 020a042571..816ff7c24a 100644 --- a/src-self-hosted/main.zig +++ b/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; diff --git a/src/all_types.hpp b/src/all_types.hpp index 7f06134c2c..797897e425 100644 --- a/src/all_types.hpp +++ b/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; }; diff --git a/src/analyze.cpp b/src/analyze.cpp index 0a408f44ff..0dc221408d 100644 --- a/src/analyze.cpp +++ b/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) { diff --git a/src/codegen.cpp b/src/codegen.cpp index 2a2cd3ba10..976b20405e 100644 --- a/src/codegen.cpp +++ b/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); diff --git a/src/ir.cpp b/src/ir.cpp index 556f255a0a..ae48c1d369 100644 --- a/src/ir.cpp +++ b/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(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(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(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: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index c0f3169b3e..1c60d68628 100644 --- a/src/ir_print.cpp +++ b/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; diff --git a/test/cases/reflection.zig b/test/cases/reflection.zig index 13adfd20cd..4227f79a04 100644 --- a/test/cases/reflection.zig +++ b/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, +}; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 6901bd302c..b2bfb9b8e4 100644 --- a/test/compile_errors.zig +++ b/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"); } -- cgit v1.2.3