ziglang / zig

General-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
https://ziglang.org
MIT License
34.92k stars 2.55k forks source link

compile error hint for *anyopaque to *T due to function pointer parameter gets the type names backwards #13586

Open andrewrk opened 1 year ago

andrewrk commented 1 year ago

Expected this test to pass:

const std = @import("std");

const S = struct {
    field: i32,
};

fn foo(ptr: *S) void {
    if (ptr.field != 1234) @panic("test failure");
}

var fp: *const fn (*anyopaque) void = undefined;

pub fn main() !void {
    fp = foo;
    var s: S = .{ .field = 1234 };
    fp(&s);
}

Instead it emits this error:

test.zig:14:10: error: expected type '*const fn(*anyopaque) void', found '*const fn(*test.S) void'
    fp = foo;
         ^~~
test.zig:14:10: note: pointer type child 'fn(*test.S) void' cannot cast into pointer type child 'fn(*anyopaque) void'
test.zig:14:10: note: parameter 0 '*test.S' cannot cast into '*anyopaque'
test.zig:14:10: note: pointer type child 'anyopaque' cannot cast into pointer type child 'test.S'
test.zig:3:11: note: struct declared here
const S = struct {
          ^~~~~~

In particular I believe this error is incorrect: test.zig:14:10: note: parameter 0 '*test.S' cannot cast into '*anyopaque' because any pointer is supposed to coerce into *anyopaque. However it is not supposed to allow a coercion the other way; *anyopaque to *T is supposed to be a compile error because this is a potentially unsafe operation which requires @ptrCast.

nektro commented 1 year ago

i think this being a compile error is correct but the types listed in the note: parameter 0 '*test.S' cannot cast into '*anyopaque' note are backwards. the followup note is more correct and discusses fp accepting an *anyopaque but being asked to pass to a *S. its failing on the assignment but this is also correct because its proving the impossibility of the fp(&s) line given the provided function.

consider this code which also fails:

const S = struct { field: i32 };

fn foo(ptr: *S) void {
    _ = ptr;
}

pub fn main() !void {
    var s: S = .{ .field = 1234 };
    foo(@as(*anyopaque, &s));
}
test.zig:9:9: error: expected type '*test.S', found '*anyopaque'
    foo(@as(*anyopaque, &s));
        ^~~~~~~~~~~~~~~~~~~
test.zig:9:9: note: pointer type child 'anyopaque' cannot cast into pointer type child 'test.S'
test.zig:1:11: note: struct declared here
const S = struct { field: i32 };
          ^~~~~~~~~~~~~~~~~~~~~
Vexu commented 1 year ago

Here's the reason:

// in Sema.coerceInMemoryAllowedFns
// Note: Cast direction is reversed here.
const param = try sema.coerceInMemoryAllowed(block, src_param_ty, dest_param_ty, false, target, dest_src, src_src);