ziglang/zig

non `callconv(.C)` function pointers no longer allowed in extern contexts

xdBronch opened this issue · 6 comments

Zig Version

0.12

Steps to Reproduce and Observed Behavior

const T = extern struct {
    f: *const fn () void,
};

export fn f(_: *const fn () void) void {}

comptime {
    _ = @as(T, undefined);
}
d.zig:5:13: error: parameter of type '*const fn () void' not allowed in function with calling convention 'C'
export fn f(_: *const fn () void) void {}
            ^~~~~~~~~~~~~~~~~~~~
d.zig:5:13: note: extern function must specify calling convention
d.zig:2:8: error: extern structs cannot contain fields of type '*const fn () void'
    f: *const fn () void,
       ^~~~~~~~~~~~~~~~~
d.zig:2:8: note: extern function must specify calling convention

this worked in 0.11, caused by #17509 see #17468 (comment)

Expected Behavior

no error

But without a callconv for extern fn you run into #19841.

What is the use case for unspecified callconv for extern fns?

its not an unspecified callconv for extern fns, its a pointer to an unspecified callconv fn being passed to a callconv(.C) function. it would be seen as an opaque pointer on the C side

On the line export fn f(_: *const fn () void) void {}, fn f does not have a callconv, so it is unspecified. The function pointer param is not the issue.

For,

const T = extern struct {
    f: *const fn () void,
};

The function pointer needs a callconv or else how would zig know what to do when you write:

const t: T = ...
t.f();

If f wasn't callable and just an *anyopaque, you are free to cast it and cause unspecified behavior.

On the line export fn f(_: *const fn () void) void {}, fn f does not have a callconv, so it is unspecified. The function pointer param is not the issue.

the export implies the C calling convention, it is not unspecified. you arent allowed to export unspecified functions like that

comptime {
    @compileLog(@typeInfo(@TypeOf(exported)).Fn.calling_convention);
    @compileLog(@typeInfo(@TypeOf(not_exported)).Fn.calling_convention);
}

export fn exported() void {}
fn not_exported() void {}
Compile Log Output:
@as(builtin.CallingConvention, .C)
@as(builtin.CallingConvention, .Unspecified)

The function pointer needs a callconv or else how would zig know what to do when you write:

no callconv means zigs callconv (or other depending on context, zig knows because zig sees and is in control of the code), the point is to be callable on the zig side but opaque on the C side

no callconv means zigs callconv (or other depending on context, zig knows because zig sees and is in control of the code), the point is to be callable on the zig side but opaque on the C side

AFAIK there is no such thing as a zig callconv for extern fn. If there were we could have extern fns with ErrorUnions

thats exactly what im saying, there isnt