Commit 0c310f0fbf

LemonBoy <thatlemon@gmail.com>
2020-03-04 10:55:34
ir: Implement @TypeOf with multiple arguments
Closes #439
1 parent 24fc69a
src/all_types.hpp
@@ -3286,7 +3286,8 @@ struct IrInstGenUnreachable {
 struct IrInstSrcTypeOf {
     IrInstSrc base;
 
-    IrInstSrc *value;
+    IrInstSrc **values;
+    size_t value_count;
 };
 
 struct IrInstSrcSetCold {
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);
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<IrInstSrcTypeOf>(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<IrInstSrc*>(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<IrInstSrc*>(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<IrInstSrc*>(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:
@@ -21662,10 +21678,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<IrInstGen*>(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);
 }
 
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, ")");
 }
 
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;
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 = .{};