When attempting to provide a function with arguments using a tuple, there is an LLMV error which aborts the compilation in some circumstances. The following code shows the behavior:
const std = @import("std");
const meta = std.meta;
const testing = std.testing;
const ActionEntry = union(State) {
off: *const fn (*Light) void,
on: *const fn (*Light, usize) void,
broken: void,
};
const State = enum(u8) {
off = 0,
on,
broken,
};
const action_map = [_]ActionEntry{
.{ .off = &Light.off_action },
.{ .on = &Light.on_action },
.{ .broken = {} },
};
pub fn action(
current_state: State,
action_args: *const anyopaque,
) void {
//std.debug.print("state: {any}, action_args: {any}\n", .{ current_state, action_args });
switch (current_state) {
inline else => |s| executeAction(s, action_args),
}
}
fn executeAction(
comptime state: State,
args: *const anyopaque,
) void {
const action_function = @field(action_map[@intFromEnum(state)], @tagName(state));
if (@TypeOf(action_function) == void) return;
const action_args: *const meta.ArgsTuple(@typeInfo(@TypeOf(action_function)).pointer.child) =
@ptrCast(@alignCast(args));
//@compileLog(action_function, action_args.*);
//std.debug.print("action_args: {any}\n", .{action_args.*});
@call(.auto, action_function, action_args.*);
}
const Light = struct {
on_count: usize = 0,
off_count: usize = 0,
fn off_action(
light: *Light,
) void {
light.off_count += 1;
std.debug.print("in off state: {d}\n", .{light.off_count});
}
fn on_action(
light: *Light,
incr: usize,
) void {
light.on_count += incr;
std.debug.print("in on state: {d}\n", .{light.on_count});
}
};
test "action dispatch" {
var light1: Light = .{};
const off_action_args: struct { *Light } = .{&light1};
action(.off, @ptrCast(&off_action_args));
try testing.expectEqual(@as(usize, 1), light1.off_count);
const on_action_args: struct { *Light, usize } = .{ &light1, 2 };
action(.on, @ptrCast(&on_action_args));
try testing.expectEqual(@as(usize, 2), light1.on_count);
const broken_action_args: struct { *Light } = .{&light1};
action(.broken, @ptrCast(&broken_action_args));
try testing.expectEqual(@as(usize, 1), light1.off_count);
try testing.expectEqual(@as(usize, 2), light1.on_count);
}
// The previous test yields:
// in off state: 1
// in on state: 2
// All 1 tests passed.
// Uncommenting the following test causes a compilation failure.
// My expectation is that it should be the same since the only
// difference is where the address of the tuple is taken.
//test "action dispatch alternate" {
// var light1: Light = .{};
// const off_action_args: *const struct { *Light } = &.{&light1};
// action(.off, @ptrCast(off_action_args));
// try testing.expectEqual(@as(usize, 1), light1.off_count);
//
// const on_action_args: *const struct { *Light, usize } = &.{ &light1, 2 };
// action(.on, @ptrCast(on_action_args));
// try testing.expectEqual(@as(usize, 2), light1.on_count);
//}
//Invalid indices for GEP pointer type!
// %10 = getelementptr inbounds { ptr }, ptr %8, i64 0, i64 0, !dbg !48869
//Invalid indices for GEP pointer type!
// %16 = getelementptr inbounds { ptr, i64 }, ptr %4, i64 0, i64 0, !dbg !48882
//Invalid indices for GEP pointer type!
// %17 = getelementptr inbounds { ptr, i64 }, ptr %4, i64 0, i64 1, !dbg !48882
//LLVM ERROR: Broken module found, compilation aborted!
// Uncommenting the following test causes test failures.
// I did _not_ expect this to compile at all since it seems to rely on
// anonymous structure types, which I thought had been removed from the language.
//test "action dispatch other" {
// var light1: Light = .{};
// const off_action_args = &.{&light1};
// action(.off, @ptrCast(off_action_args));
// try testing.expectEqual(@as(usize, 1), light1.off_count);
//
// const on_action_args = &.{ &light1, 2 };
// action(.on, @ptrCast(on_action_args));
// try testing.expectEqual(@as(usize, 2), light1.on_count);
//}
//in off state: 1 --> from test 1
//in on state: 2 --> from test 1
//in on state: 2 --> from test 1
//in off state: 1 --> from test 2, correct result
//in on state: 140732277796144 --> from test 2, incorrect value as the second argument
//expected 2, found 140732277796144
//2/2 foo.test.action dispatch other...FAIL (TestExpectedEqual)
// /home/andrewm/opt/ziglang/zig-linux-x86_64-0.14.0-dev.2340+182cdf74b/lib/std/testing.zig:97:17: 0x1040ecc in expectEqualInner__anon_259 (test)
// return error.TestExpectedEqual;
// ^
// /home/andrewm/working/micromod-zig/projects/bottom_up/litprog/parts/supporting-code/experiments/foo.zig:111:5: 0x10d1b3e in test.action dispatch other (test)
// try testing.expectEqual(@as(usize, 2), light1.on_count);
// ^
//1 passed; 0 skipped; 1 failed.
// The output of the following test is the same as the previous one:
//test "action dispatch other" {
// var light1: Light = .{};
// const off_action_args = .{&light1};
// action(.off, @ptrCast(&off_action_args));
// try testing.expectEqual(@as(usize, 1), light1.off_count);
//
// const on_action_args = .{ &light1, 2 };
// action(.on, @ptrCast(&on_action_args));
// try testing.expectEqual(@as(usize, 2), light1.on_count);
//}
Comments in the code indicate the actual output and expectations.
Zig Version
0.14.0-dev.2340+182cdf74b
Steps to Reproduce and Observed Behavior
When attempting to provide a function with arguments using a tuple, there is an LLMV error which aborts the compilation in some circumstances. The following code shows the behavior:
Expected Behavior
Comments in the code indicate the actual output and expectations.