stage2: implement builtin_call

This commit is contained in:
Veikka Tuominen 2022-02-27 18:23:50 +02:00 committed by Andrew Kelley
parent 720a5f87d4
commit 87dc60e8de
2 changed files with 85 additions and 7 deletions

View File

@ -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) {

View File

@ -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 };