authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-11-04 16:19:43-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-11-04 16:20:02-04:00
logf0d755153d90f35da612c8aa6f1853f5112f5cf1
treed27f43804e4805b78c75443cdd4ec706d37c87d8
parent4a6df04f7568dd84245b513bfd8d245f31fa8f04

add compile-time reflection for function arg types

See #383

6 files changed, 125 insertions(+), 3 deletions(-)

src/all_types.hpp+9
...@@ -1261,6 +1261,7 @@ enum BuiltinFnId {...@@ -1261,6 +1261,7 @@ enum BuiltinFnId {
1261 BuiltinFnIdAlignCast,1261 BuiltinFnIdAlignCast,
1262 BuiltinFnIdOpaqueType,1262 BuiltinFnIdOpaqueType,
1263 BuiltinFnIdSetAlignStack,1263 BuiltinFnIdSetAlignStack,
1264 BuiltinFnIdArgType,
1264};1265};
12651266
1266struct BuiltinFnEntry {1267struct BuiltinFnEntry {
...@@ -1882,6 +1883,7 @@ enum IrInstructionId {...@@ -1882,6 +1883,7 @@ enum IrInstructionId {
1882 IrInstructionIdAlignCast,1883 IrInstructionIdAlignCast,
1883 IrInstructionIdOpaqueType,1884 IrInstructionIdOpaqueType,
1884 IrInstructionIdSetAlignStack,1885 IrInstructionIdSetAlignStack,
1886 IrInstructionIdArgType,
1885};1887};
18861888
1887struct IrInstruction {1889struct IrInstruction {
...@@ -2682,6 +2684,13 @@ struct IrInstructionSetAlignStack {...@@ -2682,6 +2684,13 @@ struct IrInstructionSetAlignStack {
2682 IrInstruction *align_bytes;2684 IrInstruction *align_bytes;
2683};2685};
26842686
2687struct IrInstructionArgType {
2688 IrInstruction base;
2689
2690 IrInstruction *fn_type;
2691 IrInstruction *arg_index;
2692};
2693
2685static const size_t slice_ptr_index = 0;2694static const size_t slice_ptr_index = 0;
2686static const size_t slice_len_index = 1;2695static const size_t slice_len_index = 1;
26872696
src/codegen.cpp+3-1
...@@ -3401,6 +3401,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,...@@ -3401,6 +3401,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
3401 case IrInstructionIdPtrTypeOf:3401 case IrInstructionIdPtrTypeOf:
3402 case IrInstructionIdOpaqueType:3402 case IrInstructionIdOpaqueType:
3403 case IrInstructionIdSetAlignStack:3403 case IrInstructionIdSetAlignStack:
3404 case IrInstructionIdArgType:
3404 zig_unreachable();3405 zig_unreachable();
3405 case IrInstructionIdReturn:3406 case IrInstructionIdReturn:
3406 return ir_render_return(g, executable, (IrInstructionReturn *)instruction);3407 return ir_render_return(g, executable, (IrInstructionReturn *)instruction);
...@@ -4866,7 +4867,7 @@ static void define_builtin_fns(CodeGen *g) {...@@ -4866,7 +4867,7 @@ static void define_builtin_fns(CodeGen *g) {
4866 create_builtin_fn(g, BuiltinFnIdMaxValue, "maxValue", 1);4867 create_builtin_fn(g, BuiltinFnIdMaxValue, "maxValue", 1);
4867 create_builtin_fn(g, BuiltinFnIdMinValue, "minValue", 1);4868 create_builtin_fn(g, BuiltinFnIdMinValue, "minValue", 1);
4868 create_builtin_fn(g, BuiltinFnIdMemberCount, "memberCount", 1);4869 create_builtin_fn(g, BuiltinFnIdMemberCount, "memberCount", 1);
4869 create_builtin_fn(g, BuiltinFnIdTypeof, "typeOf", 1);4870 create_builtin_fn(g, BuiltinFnIdTypeof, "typeOf", 1); // TODO rename to TypeOf
4870 create_builtin_fn(g, BuiltinFnIdAddWithOverflow, "addWithOverflow", 4);4871 create_builtin_fn(g, BuiltinFnIdAddWithOverflow, "addWithOverflow", 4);
4871 create_builtin_fn(g, BuiltinFnIdSubWithOverflow, "subWithOverflow", 4);4872 create_builtin_fn(g, BuiltinFnIdSubWithOverflow, "subWithOverflow", 4);
4872 create_builtin_fn(g, BuiltinFnIdMulWithOverflow, "mulWithOverflow", 4);4873 create_builtin_fn(g, BuiltinFnIdMulWithOverflow, "mulWithOverflow", 4);
...@@ -4913,6 +4914,7 @@ static void define_builtin_fns(CodeGen *g) {...@@ -4913,6 +4914,7 @@ static void define_builtin_fns(CodeGen *g) {
4913 create_builtin_fn(g, BuiltinFnIdAlignCast, "alignCast", 2);4914 create_builtin_fn(g, BuiltinFnIdAlignCast, "alignCast", 2);
4914 create_builtin_fn(g, BuiltinFnIdOpaqueType, "OpaqueType", 0);4915 create_builtin_fn(g, BuiltinFnIdOpaqueType, "OpaqueType", 0);
4915 create_builtin_fn(g, BuiltinFnIdSetAlignStack, "setAlignStack", 1);4916 create_builtin_fn(g, BuiltinFnIdSetAlignStack, "setAlignStack", 1);
4917 create_builtin_fn(g, BuiltinFnIdArgType, "ArgType", 2);
4916}4918}
49174919
4918static const char *bool_to_str(bool b) {4920static const char *bool_to_str(bool b) {
src/ir.cpp+80
...@@ -567,6 +567,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSetAlignStack *)...@@ -567,6 +567,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSetAlignStack *)
567 return IrInstructionIdSetAlignStack;567 return IrInstructionIdSetAlignStack;
568}568}
569569
570static constexpr IrInstructionId ir_instruction_id(IrInstructionArgType *) {
571 return IrInstructionIdArgType;
572}
573
570template<typename T>574template<typename T>
571static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {575static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
572 T *special_instruction = allocate<T>(1);576 T *special_instruction = allocate<T>(1);
...@@ -2263,6 +2267,19 @@ static IrInstruction *ir_build_set_align_stack(IrBuilder *irb, Scope *scope, Ast...@@ -2263,6 +2267,19 @@ static IrInstruction *ir_build_set_align_stack(IrBuilder *irb, Scope *scope, Ast
2263 return &instruction->base;2267 return &instruction->base;
2264}2268}
22652269
2270static IrInstruction *ir_build_arg_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
2271 IrInstruction *fn_type, IrInstruction *arg_index)
2272{
2273 IrInstructionArgType *instruction = ir_build_instruction<IrInstructionArgType>(irb, scope, source_node);
2274 instruction->fn_type = fn_type;
2275 instruction->arg_index = arg_index;
2276
2277 ir_ref_instruction(fn_type, irb->current_basic_block);
2278 ir_ref_instruction(arg_index, irb->current_basic_block);
2279
2280 return &instruction->base;
2281}
2282
2266static IrInstruction *ir_instruction_br_get_dep(IrInstructionBr *instruction, size_t index) {2283static IrInstruction *ir_instruction_br_get_dep(IrInstructionBr *instruction, size_t index) {
2267 return nullptr;2284 return nullptr;
2268}2285}
...@@ -2992,6 +3009,14 @@ static IrInstruction *ir_instruction_setalignstack_get_dep(IrInstructionSetAlign...@@ -2992,6 +3009,14 @@ static IrInstruction *ir_instruction_setalignstack_get_dep(IrInstructionSetAlign
2992 }3009 }
2993}3010}
29943011
3012static IrInstruction *ir_instruction_argtype_get_dep(IrInstructionArgType *instruction, size_t index) {
3013 switch (index) {
3014 case 0: return instruction->fn_type;
3015 case 1: return instruction->arg_index;
3016 default: return nullptr;
3017 }
3018}
3019
2995static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t index) {3020static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t index) {
2996 switch (instruction->id) {3021 switch (instruction->id) {
2997 case IrInstructionIdInvalid:3022 case IrInstructionIdInvalid:
...@@ -3194,6 +3219,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t...@@ -3194,6 +3219,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t
3194 return ir_instruction_opaquetype_get_dep((IrInstructionOpaqueType *) instruction, index);3219 return ir_instruction_opaquetype_get_dep((IrInstructionOpaqueType *) instruction, index);
3195 case IrInstructionIdSetAlignStack:3220 case IrInstructionIdSetAlignStack:
3196 return ir_instruction_setalignstack_get_dep((IrInstructionSetAlignStack *) instruction, index);3221 return ir_instruction_setalignstack_get_dep((IrInstructionSetAlignStack *) instruction, index);
3222 case IrInstructionIdArgType:
3223 return ir_instruction_argtype_get_dep((IrInstructionArgType *) instruction, index);
3197 }3224 }
3198 zig_unreachable();3225 zig_unreachable();
3199}3226}
...@@ -4629,6 +4656,20 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4629,6 +4656,20 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
46294656
4630 return ir_build_set_align_stack(irb, scope, node, arg0_value);4657 return ir_build_set_align_stack(irb, scope, node, arg0_value);
4631 }4658 }
4659 case BuiltinFnIdArgType:
4660 {
4661 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
4662 IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
4663 if (arg0_value == irb->codegen->invalid_instruction)
4664 return arg0_value;
4665
4666 AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
4667 IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
4668 if (arg1_value == irb->codegen->invalid_instruction)
4669 return arg1_value;
4670
4671 return ir_build_arg_type(irb, scope, node, arg0_value, arg1_value);
4672 }
4632 }4673 }
4633 zig_unreachable();4674 zig_unreachable();
4634}4675}
...@@ -11686,6 +11727,13 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru...@@ -11686,6 +11727,13 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
11686 create_const_bool(ira->codegen, child_type->data.fn.fn_type_id.is_var_args),11727 create_const_bool(ira->codegen, child_type->data.fn.fn_type_id.is_var_args),
11687 ira->codegen->builtin_types.entry_bool,11728 ira->codegen->builtin_types.entry_bool,
11688 ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile);11729 ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile);
11730 } else if (buf_eql_str(field_name, "arg_count")) {
11731 bool ptr_is_const = true;
11732 bool ptr_is_volatile = false;
11733 return ir_analyze_const_ptr(ira, &field_ptr_instruction->base,
11734 create_const_usize(ira->codegen, child_type->data.fn.fn_type_id.param_count),
11735 ira->codegen->builtin_types.entry_usize,
11736 ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile);
11689 } else {11737 } else {
11690 ir_add_error(ira, &field_ptr_instruction->base,11738 ir_add_error(ira, &field_ptr_instruction->base,
11691 buf_sprintf("type '%s' has no member called '%s'",11739 buf_sprintf("type '%s' has no member called '%s'",
...@@ -15395,6 +15443,35 @@ static TypeTableEntry *ir_analyze_instruction_set_align_stack(IrAnalyze *ira, Ir...@@ -15395,6 +15443,35 @@ static TypeTableEntry *ir_analyze_instruction_set_align_stack(IrAnalyze *ira, Ir
15395 return ira->codegen->builtin_types.entry_void;15443 return ira->codegen->builtin_types.entry_void;
15396}15444}
1539715445
15446static TypeTableEntry *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstructionArgType *instruction) {
15447 IrInstruction *fn_type_inst = instruction->fn_type->other;
15448 TypeTableEntry *fn_type = ir_resolve_type(ira, fn_type_inst);
15449 if (type_is_invalid(fn_type))
15450 return ira->codegen->builtin_types.entry_invalid;
15451
15452 IrInstruction *arg_index_inst = instruction->arg_index->other;
15453 uint64_t arg_index;
15454 if (!ir_resolve_usize(ira, arg_index_inst, &arg_index))
15455 return ira->codegen->builtin_types.entry_invalid;
15456
15457 if (fn_type->id != TypeTableEntryIdFn) {
15458 ir_add_error(ira, fn_type_inst, buf_sprintf("expected function, found '%s'", buf_ptr(&fn_type->name)));
15459 return ira->codegen->builtin_types.entry_invalid;
15460 }
15461
15462 FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
15463 if (arg_index >= fn_type_id->param_count) {
15464 ir_add_error(ira, arg_index_inst,
15465 buf_sprintf("arg index %" ZIG_PRI_usize " out of bounds; '%s' has %" ZIG_PRI_usize " arguments",
15466 arg_index, buf_ptr(&fn_type->name), fn_type_id->param_count));
15467 return ira->codegen->builtin_types.entry_invalid;
15468 }
15469
15470 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
15471 out_val->data.x_type = fn_type_id->param_info[arg_index].type;
15472 return ira->codegen->builtin_types.entry_type;
15473}
15474
15398static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {15475static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
15399 switch (instruction->id) {15476 switch (instruction->id) {
15400 case IrInstructionIdInvalid:15477 case IrInstructionIdInvalid:
...@@ -15585,6 +15662,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi...@@ -15585,6 +15662,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
15585 return ir_analyze_instruction_opaque_type(ira, (IrInstructionOpaqueType *)instruction);15662 return ir_analyze_instruction_opaque_type(ira, (IrInstructionOpaqueType *)instruction);
15586 case IrInstructionIdSetAlignStack:15663 case IrInstructionIdSetAlignStack:
15587 return ir_analyze_instruction_set_align_stack(ira, (IrInstructionSetAlignStack *)instruction);15664 return ir_analyze_instruction_set_align_stack(ira, (IrInstructionSetAlignStack *)instruction);
15665 case IrInstructionIdArgType:
15666 return ir_analyze_instruction_arg_type(ira, (IrInstructionArgType *)instruction);
15588 }15667 }
15589 zig_unreachable();15668 zig_unreachable();
15590}15669}
...@@ -15765,6 +15844,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {...@@ -15765,6 +15844,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
15765 case IrInstructionIdTypeId:15844 case IrInstructionIdTypeId:
15766 case IrInstructionIdAlignCast:15845 case IrInstructionIdAlignCast:
15767 case IrInstructionIdOpaqueType:15846 case IrInstructionIdOpaqueType:
15847 case IrInstructionIdArgType:
15768 return false;15848 return false;
15769 case IrInstructionIdAsm:15849 case IrInstructionIdAsm:
15770 {15850 {
src/ir_print.cpp+12
...@@ -954,6 +954,15 @@ static void ir_print_set_align_stack(IrPrint *irp, IrInstructionSetAlignStack *i...@@ -954,6 +954,15 @@ static void ir_print_set_align_stack(IrPrint *irp, IrInstructionSetAlignStack *i
954 fprintf(irp->f, ")");954 fprintf(irp->f, ")");
955}955}
956956
957static void ir_print_arg_type(IrPrint *irp, IrInstructionArgType *instruction) {
958 fprintf(irp->f, "@ArgType(");
959 ir_print_other_instruction(irp, instruction->fn_type);
960 fprintf(irp->f, ",");
961 ir_print_other_instruction(irp, instruction->arg_index);
962 fprintf(irp->f, ")");
963}
964
965
957static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {966static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
958 ir_print_prefix(irp, instruction);967 ir_print_prefix(irp, instruction);
959 switch (instruction->id) {968 switch (instruction->id) {
...@@ -1256,6 +1265,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {...@@ -1256,6 +1265,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
1256 case IrInstructionIdSetAlignStack:1265 case IrInstructionIdSetAlignStack:
1257 ir_print_set_align_stack(irp, (IrInstructionSetAlignStack *)instruction);1266 ir_print_set_align_stack(irp, (IrInstructionSetAlignStack *)instruction);
1258 break;1267 break;
1268 case IrInstructionIdArgType:
1269 ir_print_arg_type(irp, (IrInstructionArgType *)instruction);
1270 break;
1259 }1271 }
1260 fprintf(irp->f, "\n");1272 fprintf(irp->f, "\n");
1261}1273}
test/cases/reflection.zig+7-2
...@@ -10,13 +10,18 @@ test "reflection: array, pointer, nullable, error union type child" {...@@ -10,13 +10,18 @@ test "reflection: array, pointer, nullable, error union type child" {
10 }10 }
11}11}
1212
13test "reflection: function return type and var args" {13test "reflection: function return type, var args, and param types" {
14 comptime {14 comptime {
15 assert(@typeOf(dummy).ReturnType == i32);15 assert(@typeOf(dummy).ReturnType == i32);
16 assert(!@typeOf(dummy).is_var_args);16 assert(!@typeOf(dummy).is_var_args);
17 assert(@typeOf(dummy_varargs).is_var_args);17 assert(@typeOf(dummy_varargs).is_var_args);
18 assert(@typeOf(dummy).arg_count == 3);
19 assert(@ArgType(@typeOf(dummy), 0) == bool);
20 assert(@ArgType(@typeOf(dummy), 1) == i32);
21 assert(@ArgType(@typeOf(dummy), 2) == f32);
18 }22 }
19}23}
2024
21fn dummy() -> i32 { 1234 }25fn dummy(a: bool, b: i32, c: f32) -> i32 { 1234 }
22fn dummy_varargs(args: ...) {}26fn dummy_varargs(args: ...) {}
27
test/compile_errors.zig+14
...@@ -2275,4 +2275,18 @@ pub fn addCases(cases: &tests.CompileErrorContext) {...@@ -2275,4 +2275,18 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
2275 ,2275 ,
2276 ".tmp_source.zig:2:1: error: invalid character: '\\t'");2276 ".tmp_source.zig:2:1: error: invalid character: '\\t'");
22772277
2278 cases.add("@ArgType given non function parameter",
2279 \\comptime {
2280 \\ _ = @ArgType(i32, 3);
2281 \\}
2282 ,
2283 ".tmp_source.zig:2:18: error: expected function, found 'i32'");
2284
2285 cases.add("@ArgType arg index out of bounds",
2286 \\comptime {
2287 \\ _ = @ArgType(@typeOf(add), 2);
2288 \\}
2289 \\fn add(a: i32, b: i32) -> i32 { return a + b; }
2290 ,
2291 ".tmp_source.zig:2:32: error: arg index 2 out of bounds; 'fn(i32, i32) -> i32' has 2 arguments");
2278}2292}