llvm backend ignores `@ptrCast` on comptime-known values
Zig Version
0.11.0-dev.86+b83e4d965
Steps to Reproduce and Observed Behavior
typedef struct Origin {
unsigned long x;
unsigned long y;
unsigned long z;
} Origin;
typedef struct Size {
unsigned long width;
unsigned long height;
unsigned long depth;
} Size;
typedef struct Region {
Origin origin;
Size size;
} Region;
void do_the_thing(Region in) {
// Modification is REQUIRED for the crash
in.origin.x = 0;
}
const std = @import("std");
extern "c" fn do_the_thing(*anyopaque, *anyopaque, ...) void;
const Region = extern struct {
origin: extern struct {
x: c_ulong,
y: c_ulong,
z: c_ulong,
},
size: extern struct {
width: c_ulong,
height: c_ulong,
depth: c_ulong,
},
};
pub fn main() void {
const new_fn = @ptrCast(*const fn (Region) callconv(.C) void, &do_the_thing);
@call(.{}, new_fn, .{
Region{
.origin = .{ .x = 9, .y = 9, .z = 9 },
.size = .{ .width = 9, .height = 9, .depth = 9 },
},
});
}
$ zig run -lc test.c test.zig
Segmentation fault at address 0xffffffffffffffe7
???:?:?: 0x104963c85 in _main (???)
zsh: abort ./abi-test
Expected Behavior
It should call the C function and work.
Note it works FINE if the function signature is correct to begin with, i.e. the extern "c" is properly typed.
What the #$@! am I doing this for? This looks like a really stupid thing to do. Well, this is how objc_msgSend works and I'm interfacing with Objective-C with a type signature like this. I have to cast a function pointer to a different type so that arguments are passed correctly for the C ABI.
Note that other function signatures do work fine. This specific function signature is causing the segfault.
WDYM by "other function signatures do work fine" ? which functions are you changing signatures ? What happen if you declare extern "c" fn do_the_thing(Region) void; ?
The LLVM backend is completely ignoring the @ptrCast because it is done on a comptime-known value. The issue can be worked around by making new_fn mutable.
WDYM by "other function signatures do work fine" ? which functions are you changing signatures ?
I mean that if you ptrCast other type signatures (where the func expects it), this works. I haven’t exhaustively tested type signatures but I do call at least a few dozen others and it works. This is just a specific case my application is calling that isn’t working.
What happen if you declare
extern "c" fn do_the_thing(Region) void;?
This fixes it, but this is not possible in my case due to the use case stated in the issue (obcj_msgSend). obcj_msgSend has a prototype of void f(void *, void *, …) and expects you to cast it to various other function pointer types depending on the target Objective-C selector.
The LLVM backend is completely ignoring the
@ptrCastbecause it is done on a comptime-known value. The issue can be worked around by makingnew_fnmutable.
That does work in this specific case but doesn't work in my actual application which has a more complex usage... let me try to minimize that one.
@Vexu https://github.com/ziglang/zig/issues/13605 Here is another issue where var doesn't fix it. I opened it as another issue since it seems to be slightly different since var does fix this but not that issue. If you want to merge them, feel free.
They are separate, in the second case we're not adding a byval attribute to the function call parameter, I'll give these issues more descriptive titles.
this seems to work (not crashing) on master 0.12.0-dev.1297+a9e66ed73. I did update: test.zig and tried on both linux and macos:
const std = @import("std");
extern "c" fn do_the_thing(*anyopaque, *anyopaque, ...) void;
const Region = extern struct {
origin: extern struct {
x: c_ulong,
y: c_ulong,
z: c_ulong,
},
size: extern struct {
width: c_ulong,
height: c_ulong,
depth: c_ulong,
},
};
pub fn main() void {
const new_fn = @as(*const fn (Region) callconv(.C) void, @ptrCast(&do_the_thing));
@call(.auto, new_fn, .{
Region{
.origin = .{ .x = 9, .y = 9, .z = 9 },
.size = .{ .width = 9, .height = 9, .depth = 9 },
},
});
}