| author | |
| committer | |
| log | 378bf1c3b71fdcc823fb9a5382400ec0a659fc00 |
| tree | 412c9f53fcefcb5117b89323cbf526289a4a90a0 |
| parent | 24fc69acad303d9049a99683d3bf1f41185d22db |
| parent | 116e2a93f1ab068ee46f4b7cce6b3306334cdec6 |
| signature |
8 files changed, 128 insertions(+), 18 deletions(-)
doc/langref.html.in+6-4| ... | @@ -8371,12 +8371,14 @@ test "integer truncation" { | ... | @@ -8371,12 +8371,14 @@ test "integer truncation" { |
| 8371 | {#header_close#} | 8371 | {#header_close#} |
| 8372 | 8372 | ||
| 8373 | {#header_open|@TypeOf#} | 8373 | {#header_open|@TypeOf#} |
| 8374 | <pre>{#syntax#}@TypeOf(expression) type{#endsyntax#}</pre> | 8374 | <pre>{#syntax#}@TypeOf(...) type{#endsyntax#}</pre> |
| 8375 | <p> | 8375 | <p> |
| 8376 | This function returns a compile-time constant, which is the type of the | 8376 | {#syntax#}@TypeOf{#endsyntax#} is a special builtin function that takes any (nonzero) number of expressions |
| 8377 | expression passed as an argument. The expression is evaluated. | 8377 | as parameters and returns the type of the result, using {#link|Peer Type Resolution#}. |
| 8378 | </p> | ||
| 8379 | <p> | ||
| 8380 | The expressions are evaluated, however they are guaranteed to have no <em>runtime</em> side-effects: | ||
| 8378 | </p> | 8381 | </p> |
| 8379 | <p>{#syntax#}@TypeOf{#endsyntax#} guarantees no run-time side-effects within the expression:</p> | ||
| 8380 | {#code_begin|test#} | 8382 | {#code_begin|test#} |
| 8381 | const std = @import("std"); | 8383 | const std = @import("std"); |
| 8382 | const assert = std.debug.assert; | 8384 | const assert = std.debug.assert; |
lib/std/math.zig+1-1| ... | @@ -293,7 +293,7 @@ test "math.min" { | ... | @@ -293,7 +293,7 @@ test "math.min" { |
| 293 | } | 293 | } |
| 294 | } | 294 | } |
| 295 | 295 | ||
| 296 | pub fn max(x: var, y: var) @TypeOf(x + y) { | 296 | pub fn max(x: var, y: var) @TypeOf(x, y) { |
| 297 | return if (x > y) x else y; | 297 | return if (x > y) x else y; |
| 298 | } | 298 | } |
| 299 | 299 |
src/all_types.hpp+5-1| ... | @@ -3286,7 +3286,11 @@ struct IrInstGenUnreachable { | ... | @@ -3286,7 +3286,11 @@ struct IrInstGenUnreachable { |
| 3286 | struct IrInstSrcTypeOf { | 3286 | struct IrInstSrcTypeOf { |
| 3287 | IrInstSrc base; | 3287 | IrInstSrc base; |
| 3288 | 3288 | ||
| 3289 | IrInstSrc *value; | 3289 | union { |
| 3290 | IrInstSrc *scalar; // value_count == 1 | ||
| 3291 | IrInstSrc **list; // value_count > 1 | ||
| 3292 | } value; | ||
| 3293 | size_t value_count; | ||
| 3290 | }; | 3294 | }; |
| 3291 | 3295 | ||
| 3292 | struct IrInstSrcSetCold { | 3296 | struct IrInstSrcSetCold { |
src/codegen.cpp+1-1| ... | @@ -8142,7 +8142,7 @@ static void define_builtin_fns(CodeGen *g) { | ... | @@ -8142,7 +8142,7 @@ static void define_builtin_fns(CodeGen *g) { |
| 8142 | create_builtin_fn(g, BuiltinFnIdTypeInfo, "typeInfo", 1); | 8142 | create_builtin_fn(g, BuiltinFnIdTypeInfo, "typeInfo", 1); |
| 8143 | create_builtin_fn(g, BuiltinFnIdType, "Type", 1); | 8143 | create_builtin_fn(g, BuiltinFnIdType, "Type", 1); |
| 8144 | create_builtin_fn(g, BuiltinFnIdHasField, "hasField", 2); | 8144 | create_builtin_fn(g, BuiltinFnIdHasField, "hasField", 2); |
| 8145 | create_builtin_fn(g, BuiltinFnIdTypeof, "TypeOf", 1); | 8145 | create_builtin_fn(g, BuiltinFnIdTypeof, "TypeOf", SIZE_MAX); |
| 8146 | create_builtin_fn(g, BuiltinFnIdAddWithOverflow, "addWithOverflow", 4); | 8146 | create_builtin_fn(g, BuiltinFnIdAddWithOverflow, "addWithOverflow", 4); |
| 8147 | create_builtin_fn(g, BuiltinFnIdSubWithOverflow, "subWithOverflow", 4); | 8147 | create_builtin_fn(g, BuiltinFnIdSubWithOverflow, "subWithOverflow", 4); |
| 8148 | create_builtin_fn(g, BuiltinFnIdMulWithOverflow, "mulWithOverflow", 4); | 8148 | create_builtin_fn(g, BuiltinFnIdMulWithOverflow, "mulWithOverflow", 4); |
src/ir.cpp+67-10| ... | @@ -2766,9 +2766,24 @@ static IrInstGen *ir_build_load_ptr_gen(IrAnalyze *ira, IrInst *source_instructi | ... | @@ -2766,9 +2766,24 @@ static IrInstGen *ir_build_load_ptr_gen(IrAnalyze *ira, IrInst *source_instructi |
| 2766 | return &instruction->base; | 2766 | return &instruction->base; |
| 2767 | } | 2767 | } |
| 2768 | 2768 | ||
| 2769 | static IrInstSrc *ir_build_typeof(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) { | 2769 | static IrInstSrc *ir_build_typeof_n(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, |
| 2770 | IrInstSrc **values, size_t value_count) | ||
| 2771 | { | ||
| 2772 | assert(value_count >= 2); | ||
| 2773 | |||
| 2770 | IrInstSrcTypeOf *instruction = ir_build_instruction<IrInstSrcTypeOf>(irb, scope, source_node); | 2774 | IrInstSrcTypeOf *instruction = ir_build_instruction<IrInstSrcTypeOf>(irb, scope, source_node); |
| 2771 | instruction->value = value; | 2775 | instruction->value.list = values; |
| 2776 | instruction->value_count = value_count; | ||
| 2777 | |||
| 2778 | for (size_t i = 0; i < value_count; i++) | ||
| 2779 | ir_ref_instruction(values[i], irb->current_basic_block); | ||
| 2780 | |||
| 2781 | return &instruction->base; | ||
| 2782 | } | ||
| 2783 | |||
| 2784 | static IrInstSrc *ir_build_typeof_1(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) { | ||
| 2785 | IrInstSrcTypeOf *instruction = ir_build_instruction<IrInstSrcTypeOf>(irb, scope, source_node); | ||
| 2786 | instruction->value.scalar = value; | ||
| 2772 | 2787 | ||
| 2773 | ir_ref_instruction(value, irb->current_basic_block); | 2788 | ir_ref_instruction(value, irb->current_basic_block); |
| 2774 | 2789 | ||
| ... | @@ -6043,7 +6058,7 @@ static IrInstSrc *ir_gen_fn_call_with_args(IrBuilderSrc *irb, Scope *scope, AstN | ... | @@ -6043,7 +6058,7 @@ static IrInstSrc *ir_gen_fn_call_with_args(IrBuilderSrc *irb, Scope *scope, AstN |
| 6043 | if (fn_ref == irb->codegen->invalid_inst_src) | 6058 | if (fn_ref == irb->codegen->invalid_inst_src) |
| 6044 | return fn_ref; | 6059 | return fn_ref; |
| 6045 | 6060 | ||
| 6046 | IrInstSrc *fn_type = ir_build_typeof(irb, scope, source_node, fn_ref); | 6061 | IrInstSrc *fn_type = ir_build_typeof_1(irb, scope, source_node, fn_ref); |
| 6047 | 6062 | ||
| 6048 | IrInstSrc **args = heap::c_allocator.allocate<IrInstSrc*>(args_len); | 6063 | IrInstSrc **args = heap::c_allocator.allocate<IrInstSrc*>(args_len); |
| 6049 | for (size_t i = 0; i < args_len; i += 1) { | 6064 | for (size_t i = 0; i < args_len; i += 1) { |
| ... | @@ -6104,12 +6119,33 @@ static IrInstSrc *ir_gen_builtin_fn_call(IrBuilderSrc *irb, Scope *scope, AstNod | ... | @@ -6104,12 +6119,33 @@ static IrInstSrc *ir_gen_builtin_fn_call(IrBuilderSrc *irb, Scope *scope, AstNod |
| 6104 | { | 6119 | { |
| 6105 | Scope *sub_scope = create_typeof_scope(irb->codegen, node, scope); | 6120 | Scope *sub_scope = create_typeof_scope(irb->codegen, node, scope); |
| 6106 | 6121 | ||
| 6107 | AstNode *arg_node = node->data.fn_call_expr.params.at(0); | 6122 | size_t arg_count = node->data.fn_call_expr.params.length; |
| 6108 | IrInstSrc *arg = ir_gen_node(irb, arg_node, sub_scope); | 6123 | |
| 6109 | if (arg == irb->codegen->invalid_inst_src) | 6124 | IrInstSrc *type_of; |
| 6110 | return arg; | 6125 | |
| 6126 | if (arg_count == 0) { | ||
| 6127 | add_node_error(irb->codegen, node, | ||
| 6128 | buf_sprintf("expected at least 1 argument, found 0")); | ||
| 6129 | return irb->codegen->invalid_inst_src; | ||
| 6130 | } else if (arg_count == 1) { | ||
| 6131 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); | ||
| 6132 | IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, sub_scope); | ||
| 6133 | if (arg0_value == irb->codegen->invalid_inst_src) | ||
| 6134 | return arg0_value; | ||
| 6111 | 6135 | ||
| 6112 | IrInstSrc *type_of = ir_build_typeof(irb, scope, node, arg); | 6136 | type_of = ir_build_typeof_1(irb, scope, node, arg0_value); |
| 6137 | } else { | ||
| 6138 | IrInstSrc **args = heap::c_allocator.allocate<IrInstSrc*>(arg_count); | ||
| 6139 | for (size_t i = 0; i < arg_count; i += 1) { | ||
| 6140 | AstNode *arg_node = node->data.fn_call_expr.params.at(i); | ||
| 6141 | IrInstSrc *arg = ir_gen_node(irb, arg_node, sub_scope); | ||
| 6142 | if (arg == irb->codegen->invalid_inst_src) | ||
| 6143 | return irb->codegen->invalid_inst_src; | ||
| 6144 | args[i] = arg; | ||
| 6145 | } | ||
| 6146 | |||
| 6147 | type_of = ir_build_typeof_n(irb, scope, node, args, arg_count); | ||
| 6148 | } | ||
| 6113 | return ir_lval_wrap(irb, scope, type_of, lval, result_loc); | 6149 | return ir_lval_wrap(irb, scope, type_of, lval, result_loc); |
| 6114 | } | 6150 | } |
| 6115 | case BuiltinFnIdSetCold: | 6151 | case BuiltinFnIdSetCold: |
| ... | @@ -21662,10 +21698,31 @@ static IrInstGen *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstSrcLoadP | ... | @@ -21662,10 +21698,31 @@ static IrInstGen *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstSrcLoadP |
| 21662 | } | 21698 | } |
| 21663 | 21699 | ||
| 21664 | static IrInstGen *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstSrcTypeOf *typeof_instruction) { | 21700 | static IrInstGen *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstSrcTypeOf *typeof_instruction) { |
| 21665 | IrInstGen *expr_value = typeof_instruction->value->child; | 21701 | ZigType *type_entry; |
| 21666 | ZigType *type_entry = expr_value->value->type; | 21702 | |
| 21703 | const size_t value_count = typeof_instruction->value_count; | ||
| 21704 | |||
| 21705 | // Fast path for the common case of TypeOf with a single argument | ||
| 21706 | if (value_count < 2) { | ||
| 21707 | type_entry = typeof_instruction->value.scalar->child->value->type; | ||
| 21708 | } else { | ||
| 21709 | IrInstGen **args = heap::c_allocator.allocate<IrInstGen*>(value_count); | ||
| 21710 | for (size_t i = 0; i < value_count; i += 1) { | ||
| 21711 | IrInstGen *value = typeof_instruction->value.list[i]->child; | ||
| 21712 | if (type_is_invalid(value->value->type)) | ||
| 21713 | return ira->codegen->invalid_inst_gen; | ||
| 21714 | args[i] = value; | ||
| 21715 | } | ||
| 21716 | |||
| 21717 | type_entry = ir_resolve_peer_types(ira, typeof_instruction->base.base.source_node, | ||
| 21718 | nullptr, args, value_count); | ||
| 21719 | |||
| 21720 | heap::c_allocator.deallocate(args, value_count); | ||
| 21721 | } | ||
| 21722 | |||
| 21667 | if (type_is_invalid(type_entry)) | 21723 | if (type_is_invalid(type_entry)) |
| 21668 | return ira->codegen->invalid_inst_gen; | 21724 | return ira->codegen->invalid_inst_gen; |
| 21725 | |||
| 21669 | return ir_const_type(ira, &typeof_instruction->base.base, type_entry); | 21726 | return ir_const_type(ira, &typeof_instruction->base.base, type_entry); |
| 21670 | } | 21727 | } |
| 21671 | 21728 |
src/ir_print.cpp+7-1| ... | @@ -1118,7 +1118,13 @@ static void ir_print_vector_store_elem(IrPrintGen *irp, IrInstGenVectorStoreElem | ... | @@ -1118,7 +1118,13 @@ static void ir_print_vector_store_elem(IrPrintGen *irp, IrInstGenVectorStoreElem |
| 1118 | 1118 | ||
| 1119 | static void ir_print_typeof(IrPrintSrc *irp, IrInstSrcTypeOf *instruction) { | 1119 | static void ir_print_typeof(IrPrintSrc *irp, IrInstSrcTypeOf *instruction) { |
| 1120 | fprintf(irp->f, "@TypeOf("); | 1120 | fprintf(irp->f, "@TypeOf("); |
| 1121 | ir_print_other_inst_src(irp, instruction->value); | 1121 | if (instruction->value_count == 1) { |
| 1122 | ir_print_other_inst_src(irp, instruction->value.scalar); | ||
| 1123 | } else { | ||
| 1124 | for (size_t i = 0; i < instruction->value_count; i += 1) { | ||
| 1125 | ir_print_other_inst_src(irp, instruction->value.list[i]); | ||
| 1126 | } | ||
| 1127 | } | ||
| 1122 | fprintf(irp->f, ")"); | 1128 | fprintf(irp->f, ")"); |
| 1123 | } | 1129 | } |
| 1124 | 1130 |
test/compile_errors.zig+18| ... | @@ -2,6 +2,24 @@ const tests = @import("tests.zig"); | ... | @@ -2,6 +2,24 @@ const tests = @import("tests.zig"); |
| 2 | const std = @import("std"); | 2 | const std = @import("std"); |
| 3 | 3 | ||
| 4 | pub fn addCases(cases: *tests.CompileErrorContext) void { | 4 | pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5 | cases.addTest("@TypeOf with no arguments", | ||
| 6 | \\export fn entry() void { | ||
| 7 | \\ _ = @TypeOf(); | ||
| 8 | \\} | ||
| 9 | , &[_][]const u8{ | ||
| 10 | "tmp.zig:2:9: error: expected at least 1 argument, found 0", | ||
| 11 | }); | ||
| 12 | |||
| 13 | cases.addTest("@TypeOf with incompatible arguments", | ||
| 14 | \\export fn entry() void { | ||
| 15 | \\ var var_1: f32 = undefined; | ||
| 16 | \\ var var_2: u32 = undefined; | ||
| 17 | \\ _ = @TypeOf(var_1, var_2); | ||
| 18 | \\} | ||
| 19 | , &[_][]const u8{ | ||
| 20 | "tmp.zig:4:9: error: incompatible types: 'f32' and 'u32'", | ||
| 21 | }); | ||
| 22 | |||
| 5 | cases.addTest("type mismatch with tuple concatenation", | 23 | cases.addTest("type mismatch with tuple concatenation", |
| 6 | \\export fn entry() void { | 24 | \\export fn entry() void { |
| 7 | \\ var x = .{}; | 25 | \\ var x = .{}; |
test/stage1/behavior/sizeof_and_typeof.zig+23| ... | @@ -105,6 +105,29 @@ test "@TypeOf() has no runtime side effects" { | ... | @@ -105,6 +105,29 @@ test "@TypeOf() has no runtime side effects" { |
| 105 | expect(data == 0); | 105 | expect(data == 0); |
| 106 | } | 106 | } |
| 107 | 107 | ||
| 108 | test "@TypeOf() with multiple arguments" { | ||
| 109 | { | ||
| 110 | var var_1: u32 = undefined; | ||
| 111 | var var_2: u8 = undefined; | ||
| 112 | var var_3: u64 = undefined; | ||
| 113 | comptime expect(@TypeOf(var_1, var_2, var_3) == u64); | ||
| 114 | } | ||
| 115 | { | ||
| 116 | var var_1: f16 = undefined; | ||
| 117 | var var_2: f32 = undefined; | ||
| 118 | var var_3: f64 = undefined; | ||
| 119 | comptime expect(@TypeOf(var_1, var_2, var_3) == f64); | ||
| 120 | } | ||
| 121 | { | ||
| 122 | var var_1: u16 = undefined; | ||
| 123 | comptime expect(@TypeOf(var_1, 0xffff) == u16); | ||
| 124 | } | ||
| 125 | { | ||
| 126 | var var_1: f32 = undefined; | ||
| 127 | comptime expect(@TypeOf(var_1, 3.1415) == f32); | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 108 | test "branching logic inside @TypeOf" { | 131 | test "branching logic inside @TypeOf" { |
| 109 | const S = struct { | 132 | const S = struct { |
| 110 | var data: i32 = 0; | 133 | var data: i32 = 0; |