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