From 5f40c15f0fd4bacfc48c5fd54f4cf97eec18ad86 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Wed, 4 Mar 2020 10:55:34 +0100 Subject: [PATCH 1/3] ir: Implement @TypeOf with multiple arguments Closes #439 --- src/all_types.hpp | 3 +- src/codegen.cpp | 2 +- src/ir.cpp | 59 ++++++++++++++++++---- src/ir_print.cpp | 2 +- test/compile_errors.zig | 18 +++++++ test/stage1/behavior/sizeof_and_typeof.zig | 23 +++++++++ 6 files changed, 93 insertions(+), 14 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 9f6bfd80a582..d1fc03f8a17d 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -3286,7 +3286,8 @@ struct IrInstGenUnreachable { struct IrInstSrcTypeOf { IrInstSrc base; - IrInstSrc *value; + IrInstSrc **values; + size_t value_count; }; struct IrInstSrcSetCold { diff --git a/src/codegen.cpp b/src/codegen.cpp index 107b9798a299..c91a363d2f45 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -8142,7 +8142,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdTypeInfo, "typeInfo", 1); create_builtin_fn(g, BuiltinFnIdType, "Type", 1); create_builtin_fn(g, BuiltinFnIdHasField, "hasField", 2); - create_builtin_fn(g, BuiltinFnIdTypeof, "TypeOf", 1); + create_builtin_fn(g, BuiltinFnIdTypeof, "TypeOf", SIZE_MAX); create_builtin_fn(g, BuiltinFnIdAddWithOverflow, "addWithOverflow", 4); create_builtin_fn(g, BuiltinFnIdSubWithOverflow, "subWithOverflow", 4); create_builtin_fn(g, BuiltinFnIdMulWithOverflow, "mulWithOverflow", 4); diff --git a/src/ir.cpp b/src/ir.cpp index 72e247b75a55..0c8b1338c59f 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -2766,11 +2766,13 @@ static IrInstGen *ir_build_load_ptr_gen(IrAnalyze *ira, IrInst *source_instructi return &instruction->base; } -static IrInstSrc *ir_build_typeof(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) { +static IrInstSrc *ir_build_typeof(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc **values, size_t value_count) { IrInstSrcTypeOf *instruction = ir_build_instruction(irb, scope, source_node); - instruction->value = value; + instruction->values = values; + instruction->value_count = value_count; - ir_ref_instruction(value, irb->current_basic_block); + for (size_t i = 0; i < value_count; i++) + ir_ref_instruction(values[i], irb->current_basic_block); return &instruction->base; } @@ -6043,7 +6045,9 @@ static IrInstSrc *ir_gen_fn_call_with_args(IrBuilderSrc *irb, Scope *scope, AstN if (fn_ref == irb->codegen->invalid_inst_src) return fn_ref; - IrInstSrc *fn_type = ir_build_typeof(irb, scope, source_node, fn_ref); + IrInstSrc **typeof_args = heap::c_allocator.allocate(1); + typeof_args[0] = fn_ref; + IrInstSrc *fn_type = ir_build_typeof(irb, scope, source_node, typeof_args, 1); IrInstSrc **args = heap::c_allocator.allocate(args_len); for (size_t i = 0; i < args_len; i += 1) { @@ -6104,12 +6108,24 @@ static IrInstSrc *ir_gen_builtin_fn_call(IrBuilderSrc *irb, Scope *scope, AstNod { Scope *sub_scope = create_typeof_scope(irb->codegen, node, scope); - AstNode *arg_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg = ir_gen_node(irb, arg_node, sub_scope); - if (arg == irb->codegen->invalid_inst_src) - return arg; + size_t arg_count = node->data.fn_call_expr.params.length; + + if (arg_count < 1) { + add_node_error(irb->codegen, node, + buf_sprintf("expected at least 1 argument, found 0")); + return irb->codegen->invalid_inst_src; + } + + IrInstSrc **args = heap::c_allocator.allocate(arg_count); + for (size_t i = 0; i < arg_count; i += 1) { + AstNode *arg_node = node->data.fn_call_expr.params.at(i); + IrInstSrc *arg = ir_gen_node(irb, arg_node, sub_scope); + if (arg == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + args[i] = arg; + } - IrInstSrc *type_of = ir_build_typeof(irb, scope, node, arg); + IrInstSrc *type_of = ir_build_typeof(irb, scope, node, args, arg_count); return ir_lval_wrap(irb, scope, type_of, lval, result_loc); } case BuiltinFnIdSetCold: @@ -21663,10 +21679,31 @@ static IrInstGen *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstSrcLoadP } static IrInstGen *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstSrcTypeOf *typeof_instruction) { - IrInstGen *expr_value = typeof_instruction->value->child; - ZigType *type_entry = expr_value->value->type; + ZigType *type_entry; + + const size_t value_count = typeof_instruction->value_count; + + // Fast path for the common case of TypeOf with a single argument + if (value_count < 2) { + type_entry = typeof_instruction->values[0]->child->value->type; + } else { + IrInstGen **args = heap::c_allocator.allocate(value_count); + for (size_t i = 0; i < value_count; i += 1) { + IrInstGen *value = typeof_instruction->values[i]->child; + if (type_is_invalid(value->value->type)) + return ira->codegen->invalid_inst_gen; + args[i] = value; + } + + type_entry = ir_resolve_peer_types(ira, typeof_instruction->base.base.source_node, + nullptr, args, value_count); + + heap::c_allocator.deallocate(args, value_count); + } + if (type_is_invalid(type_entry)) return ira->codegen->invalid_inst_gen; + return ir_const_type(ira, &typeof_instruction->base.base, type_entry); } diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 1cfe90466012..57469c4767ea 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1118,7 +1118,7 @@ static void ir_print_vector_store_elem(IrPrintGen *irp, IrInstGenVectorStoreElem static void ir_print_typeof(IrPrintSrc *irp, IrInstSrcTypeOf *instruction) { fprintf(irp->f, "@TypeOf("); - ir_print_other_inst_src(irp, instruction->value); + // ir_print_other_inst_src(irp, instruction->value); fprintf(irp->f, ")"); } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 979bf45bbe82..f55931154771 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2,6 +2,24 @@ const tests = @import("tests.zig"); const std = @import("std"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.addTest("@TypeOf with no arguments", + \\export fn entry() void { + \\ _ = @TypeOf(); + \\} + , &[_][]const u8{ + "tmp.zig:2:9: error: expected at least 1 argument, found 0", + }); + + cases.addTest("@TypeOf with incompatible arguments", + \\export fn entry() void { + \\ var var_1: f32 = undefined; + \\ var var_2: u32 = undefined; + \\ _ = @TypeOf(var_1, var_2); + \\} + , &[_][]const u8{ + "tmp.zig:4:9: error: incompatible types: 'f32' and 'u32'", + }); + cases.addTest("type mismatch with tuple concatenation", \\export fn entry() void { \\ var x = .{}; diff --git a/test/stage1/behavior/sizeof_and_typeof.zig b/test/stage1/behavior/sizeof_and_typeof.zig index 322c5fbbad2b..5f9dc8ba5e81 100644 --- a/test/stage1/behavior/sizeof_and_typeof.zig +++ b/test/stage1/behavior/sizeof_and_typeof.zig @@ -105,6 +105,29 @@ test "@TypeOf() has no runtime side effects" { expect(data == 0); } +test "@TypeOf() with multiple arguments" { + { + var var_1: u32 = undefined; + var var_2: u8 = undefined; + var var_3: u64 = undefined; + comptime expect(@TypeOf(var_1, var_2, var_3) == u64); + } + { + var var_1: f16 = undefined; + var var_2: f32 = undefined; + var var_3: f64 = undefined; + comptime expect(@TypeOf(var_1, var_2, var_3) == f64); + } + { + var var_1: u16 = undefined; + comptime expect(@TypeOf(var_1, 0xffff) == u16); + } + { + var var_1: f32 = undefined; + comptime expect(@TypeOf(var_1, 3.1415) == f32); + } +} + test "branching logic inside @TypeOf" { const S = struct { var data: i32 = 0; From cf188197ab9a593775eba372c3be278caee5a10d Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Wed, 4 Mar 2020 10:56:01 +0100 Subject: [PATCH 2/3] std: Use @TypeOf(x,y) as return value for max --- lib/std/math.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/math.zig b/lib/std/math.zig index c99de8f9dc57..9a1143a17d54 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -293,7 +293,7 @@ test "math.min" { } } -pub fn max(x: var, y: var) @TypeOf(x + y) { +pub fn max(x: var, y: var) @TypeOf(x, y) { return if (x > y) x else y; } From 85a5468fc8e469c0d45a5683d34b7330cc02cd3c Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Wed, 4 Mar 2020 11:00:09 +0100 Subject: [PATCH 3/3] ir: Adapt ir_print for the new @TypeOf format --- src/ir_print.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 57469c4767ea..744dcf673d5c 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1118,7 +1118,9 @@ static void ir_print_vector_store_elem(IrPrintGen *irp, IrInstGenVectorStoreElem static void ir_print_typeof(IrPrintSrc *irp, IrInstSrcTypeOf *instruction) { fprintf(irp->f, "@TypeOf("); - // ir_print_other_inst_src(irp, instruction->value); + for (size_t i = 0; i < instruction->value_count; i += 1) { + ir_print_other_inst_src(irp, instruction->values[i]); + } fprintf(irp->f, ")"); }