Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

deprecate @bitCast #1992

Closed
andrewrk opened this issue Feb 20, 2019 · 1 comment
Closed

deprecate @bitCast #1992

andrewrk opened this issue Feb 20, 2019 · 1 comment
Labels
proposal This issue suggests modifications. If it also has the "accepted" label then it is planned.
Milestone

Comments

@andrewrk
Copy link
Member

andrewrk commented Feb 20, 2019

Now that it's certain the Zig specification (#75) will have a well-defined memory model, it's perfectly legal for userland code to do this:

fn bitCast(comptime Dest: type, value: var) Dest {
    return @ptrCast(*Dest, &value).*;
}

One reason perhaps to not do this, is that with copy elision (#287), @bitCast can participate in a no-copy expression, like this:

const Foo = packed struct {
    number: u16,
};
const Bar = packed struct {
    half1: u8,
    half2: u8,
};
export fn entry(n: u16) u8 {
    var x = @bitCast(Bar, Foo{
        .number = n,
    });
    return x.half2;
}

With copy-elision, the initialization of x directly initializes a Bar, without a step in between. Contrasted with a userland function:

fn bitCast(comptime Dest: type, value: var) Dest {
    return @ptrCast(*Dest, &value).*;
}
export fn entry(n: u16) u8 {
    var x = bitCast(Bar, Foo{
        .number = n,
    });
    return x.half2;
}

Now the Foo initialization operates on stack memory which is then passed to the userland bitCast function, and the result is memory-loaded into x.

To achieve the same semantics in userland, one would have to do this:

export fn entry(n: u16) u8 {
    var x: Bar = undefined;
    @ptrCast(*Foo, &x).* = Foo{
        .number = n,
    };
    return x.half2;
}
@andrewrk andrewrk added the proposal This issue suggests modifications. If it also has the "accepted" label then it is planned. label Feb 20, 2019
@andrewrk andrewrk added this to the 0.5.0 milestone Feb 20, 2019
@rohlem
Copy link
Contributor

rohlem commented Feb 21, 2019

While it might be up to taste, I personally like the immediacy and self-explanatory nature of @bitCast - going an extra step via &.* hides the intention, but std.bitCast of course works as well.

I'm slightly concerned however that, if @ptrCast and bit cast are so similar in nature, and there is no more lingual difference (both use @ptrCast), one could easily mismatch the pointer levels with multiple layers of indirection:

fn foo(a: *[*]u32){
 var b = @ptrCast([*]i32, a); //either meant *[*]i32, or forgot to dereference a
}

I only now realized that status-quo Zig lets this pass without an error.

Maybe (if the note on copy elision optimization were to be resolved) @ptrCast works as the underlying primitive, and std should have both ptrCast, which explicitly checks that the pointer levels match, and bitCast, which explicitly checks you're not casting between pointers? (That way you have to decide to use std.ptrCast to opt in to checks, or @ptrCast to opt out.)
In that case I would also suggest putting a reference to std.ptrCast and std.bitCast into the documentation of @ptrCast - especially since the language doc is currently the only doc around.

@andrewrk andrewrk modified the milestones: 0.5.0, 0.6.0 Apr 30, 2019
@andrewrk andrewrk modified the milestones: 0.6.0, 0.7.0 Dec 31, 2019
@andrewrk andrewrk modified the milestones: 0.7.0, 0.8.0 Oct 27, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
proposal This issue suggests modifications. If it also has the "accepted" label then it is planned.
Projects
None yet
Development

No branches or pull requests

2 participants