zig icon indicating copy to clipboard operation
zig copied to clipboard

false positive `@bitCast size mismatch` compile error when using sentineled array

Open andrewrk opened this issue 3 years ago • 4 comments

Zig Version

0.11.0-dev.1199+1878bdfbb

Steps to Reproduce and Observed Behavior

const std = @import("std");
const expect = std.testing.expect;

test "@bitCast size with sentineled array" {
    const x = @bitCast(u64, "Location".*);
    const y = std.mem.readIntNative(u64, "Location");
    try expect(x == y);
}
test3.zig:5:39: error: @bitCast size mismatch: destination type 'u64' has 64 bits but source type '[8:0]u8' has 72 bits
    const x = @bitCast(u64, "Location".*);
                            ~~~~~~~~~~^~

Expected Behavior

Test passed.

andrewrk avatar Jan 05 '23 22:01 andrewrk

Is the sentinel always ignored or would @bitCast(u64, "Locatio".*) also work?

Vexu avatar Jan 05 '23 22:01 Vexu

Hmmm. I think the sentinel would be ignored, making your example a compile error. To not ignore it, one would do this:

const std = @import("std");
const expect = std.testing.expect;

test "@bitCast size with sentineled array, including sentinel" {
    const x = @bitCast(u64, "Locatio"[0..8].*);
    const y = std.mem.readIntNative(u64, "Locatio\x00");
    try expect(x == y);
}
$ stage4/bin/zig test test3.zig 
Test [1/1] test.@bitCast size with sentineled array... FAIL (TestUnexpectedResult)
/home/andy/dev/zig/lib/std/testing.zig:509:14: 0x20bec7 in expect (test)
    if (!ok) return error.TestUnexpectedResult;
             ^
/home/andy/dev/zig/build-release/test3.zig:7:5: 0x20c036 in test.@bitCast size with sentineled array (test)
    try expect(x == y);
    ^
0 passed; 0 skipped; 1 failed.

I would expect this test to pass as well.

andrewrk avatar Jan 05 '23 22:01 andrewrk

That seems good enough. One downside to this is that it introduces an inconsistency to @bitCast:

// in @bitCast(A, @as(B, b))
if (B == sentineled array)
    assert(@sizeOf(A) == @sizeOf(B) - @sizeOf(B.Elem))
else
    assert(@sizeOf(A) == @sizeOf(B));

Vexu avatar Jan 05 '23 22:01 Vexu

I want to update @bitCast to semantically operate at a "bit-level". Instead of being defined in terms of @ptrCast, I think @bitCast should be defined something like this:

  • in an abstract sense, explode out the value into its bits. for example, a u32 has lsb to msb which are independent of endianness.
  • reinterpret those bits using a different type
  • reconstruct those bits into the new type

This has a couple of nice properties:

  • bitcasting between floats and integers works independently of endianness, which is a property I believe we already rely on.
  • bitcasting between, for example, f80 and u80 would work as one expects, even when f80 is represented on the target as a 128-bit struct with padding.

cc @SpexGuy I think we discussed this a long time ago but did not reach a conclusion, or perhaps I have reverted to a pre-discussion understanding of the situation.

andrewrk avatar Jan 05 '23 22:01 andrewrk