Commit 87dc60e8de

Veikka Tuominen <git@vexu.eu>
2022-02-27 17:23:50
stage2: implement builtin_call
1 parent 720a5f8
Changed files (2)
src
test
behavior
src/Sema.zig
@@ -13154,9 +13154,59 @@ fn zirMulAdd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
 }
 
 fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
+    const tracy = trace(@src());
+    defer tracy.end();
+
     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
-    const src = inst_data.src();
-    return sema.fail(block, src, "TODO: Sema.zirBuiltinCall", .{});
+    const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
+    const func_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
+    const args_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
+    const call_src = inst_data.src();
+
+    const extra = sema.code.extraData(Zir.Inst.BuiltinCall, inst_data.payload_index);
+    var func = sema.resolveInst(extra.data.callee);
+    const options = sema.resolveInst(extra.data.options);
+    const args = sema.resolveInst(extra.data.args);
+
+    const modifier: std.builtin.CallOptions.Modifier = modifier: {
+        const export_options_ty = try sema.getBuiltinType(block, options_src, "CallOptions");
+        const coerced_options = try sema.coerce(block, export_options_ty, options, options_src);
+        const options_val = try sema.resolveConstValue(block, options_src, coerced_options);
+        const fields = options_val.castTag(.@"struct").?.data;
+        const struct_obj = export_options_ty.castTag(.@"struct").?.data;
+        const modifier_index = struct_obj.fields.getIndex("modifier").?;
+        const stack_index = struct_obj.fields.getIndex("stack").?;
+        if (!fields[stack_index].isNull()) {
+            return sema.fail(block, options_src, "TODO: implement @call with stack", .{});
+        }
+        break :modifier fields[modifier_index].toEnum(std.builtin.CallOptions.Modifier);
+    };
+
+    const args_ty = sema.typeOf(args);
+    if (!args_ty.isTuple() and args_ty.tag() != .empty_struct_literal) {
+        return sema.fail(block, args_src, "expected a tuple, found {}", .{args_ty});
+    }
+
+    var resolved_args: []Air.Inst.Ref = undefined;
+
+    // Desugar bound functions here
+    if (sema.typeOf(func).tag() == .bound_fn) {
+        const bound_func = try sema.resolveValue(block, func_src, func);
+        const bound_data = &bound_func.cast(Value.Payload.BoundFn).?.data;
+        func = bound_data.func_inst;
+        resolved_args = try sema.arena.alloc(Air.Inst.Ref, args_ty.structFieldCount() + 1);
+        resolved_args[0] = bound_data.arg0_inst;
+        for (resolved_args[1..]) |*resolved, i| {
+            resolved.* = try sema.tupleFieldValByIndex(block, args_src, args, @intCast(u32, i), args_ty);
+        }
+    } else {
+        resolved_args = try sema.arena.alloc(Air.Inst.Ref, args_ty.structFieldCount());
+        for (resolved_args) |*resolved, i| {
+            resolved.* = try sema.tupleFieldValByIndex(block, args_src, args, @intCast(u32, i), args_ty);
+        }
+    }
+
+    return sema.analyzeCall(block, func, func_src, call_src, modifier, false, resolved_args);
 }
 
 fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -14684,10 +14734,8 @@ fn tupleFieldVal(
     field_name_src: LazySrcLoc,
     tuple_ty: Type,
 ) CompileError!Air.Inst.Ref {
-    const tuple = tuple_ty.castTag(.tuple).?.data;
-
     if (mem.eql(u8, field_name, "len")) {
-        return sema.addIntUnsigned(Type.usize, tuple.types.len);
+        return sema.addIntUnsigned(Type.usize, tuple_ty.structFieldCount());
     }
 
     const field_index = std.fmt.parseUnsigned(u32, field_name, 10) catch |err| {
@@ -14695,7 +14743,18 @@ fn tupleFieldVal(
             tuple_ty, field_name, @errorName(err),
         });
     };
+    return tupleFieldValByIndex(sema, block, src, tuple_byval, field_index, tuple_ty);
+}
 
+fn tupleFieldValByIndex(
+    sema: *Sema,
+    block: *Block,
+    src: LazySrcLoc,
+    tuple_byval: Air.Inst.Ref,
+    field_index: u32,
+    tuple_ty: Type,
+) CompileError!Air.Inst.Ref {
+    const tuple = tuple_ty.castTag(.tuple).?.data;
     const field_ty = tuple.types[field_index];
 
     if (tuple.values[field_index].tag() != .unreachable_value) {
test/behavior/call.zig
@@ -3,6 +3,21 @@ const std = @import("std");
 const expect = std.testing.expect;
 const expectEqual = std.testing.expectEqual;
 
+test "super basic invocations" {
+    const foo = struct {
+        fn foo() i32 {
+            return 1234;
+        }
+    }.foo;
+    try expect(@call(.{}, foo, .{}) == 1234);
+    comptime try expect(@call(.{ .modifier = .always_inline }, foo, .{}) == 1234);
+    {
+        // comptime call without comptime keyword
+        const result = @call(.{ .modifier = .compile_time }, foo, .{}) == 1234;
+        comptime try expect(result);
+    }
+}
+
 test "basic invocations" {
     if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
 
@@ -34,7 +49,10 @@ test "basic invocations" {
 }
 
 test "tuple parameters" {
-    if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
 
     const add = struct {
         fn add(a: i32, b: i32) i32 {
@@ -47,7 +65,8 @@ test "tuple parameters" {
     try expect(@call(.{}, add, .{ 12, b }) == 46);
     try expect(@call(.{}, add, .{ a, b }) == 46);
     try expect(@call(.{}, add, .{ 12, 34 }) == 46);
-    comptime try expect(@call(.{}, add, .{ 12, 34 }) == 46);
+    if (builtin.zig_backend == .stage1) comptime try expect(@call(.{}, add, .{ 12, 34 }) == 46); // TODO
+    try expect(comptime @call(.{}, add, .{ 12, 34 }) == 46);
     {
         const separate_args0 = .{ a, b };
         const separate_args1 = .{ a, 34 };