Skip to content

Conversation

fu5ha
Copy link
Collaborator

@fu5ha fu5ha commented Mar 29, 2022

This adds support for unions in the derive macros.

The logic goes:

  • All unions are AnyBitPattern because actually accessing them is unsafe anyhow
  • Unions are NoPadding (and Pod, since they're always AnyBitPattern) if
    • All their fields are NoPadding
    • All their fields are the same size (therefore no padding added at the end of any variants)

@JakobDegen
Copy link

* Unions are `NoPadding` (and `Pod`, since they're always `AnyBitPattern`) if
  
  * All their fields are `NoPadding`
  * All their fields are the same size (therefore no padding added at the end of any variants)

This is probably not correct. The following code passes miri:

fn main() {
    union U {
        _a: bool,
    }
    unsafe {
        let _u: U = core::mem::MaybeUninit::uninit().assume_init();   
    }
}

The current stance of UCG is somewhat undecided, but the issue seems to lean towards "all data is accepted for all unions." Of course, it still requires unsafe code to get the uninit bytes into the union, but still I don't think this impl should exist.

@fu5ha
Copy link
Collaborator Author

fu5ha commented Mar 29, 2022

:( That is... quite inconvenient

Of course, it still requires unsafe code to get the uninit bytes into the union, but still I don't think this impl should exist.

My view is that if you use unsafe to do this sort of thing then all bets are off, so I still think that the impl is fine

@JakobDegen
Copy link

JakobDegen commented Mar 29, 2022

I don't think that's the case at all, because such unsafe code can be behind a sound API.

// Library crate A
#[derive(NoPadding)]
union U {
    _a: bool,
    b: u8,
}

// Library crate B
pub fn lib_fn(x: i32) -> (bool, U) {
    if x == 0 {
        (true, U { b: '0' })
    } else {
        // In this case we don't care what `U` is
        (false, MaybeUninit::uninit().assume_init())
    }
}

// Upstream
fn main() {
    let (_, u) = lib_fn(1);
    dbg!(bytemuck::transmute::<_, u8>(u))
}

The only unsafe code here is in library crate B, but I don't see how you can argue that it's unsound - especially given that it might have been written before the derive was added

@Lokathor
Copy link
Owner

I think we need to go with JakobDegen on this one. All unions are effectively entirely padding, in a sense, at least for the purposes of our traits. Since all bytes of a union can be uninit, we can't have them be NoPadding.

@fu5ha
Copy link
Collaborator Author

fu5ha commented Mar 29, 2022

hm, I can buy that I think. I will remove the impls that depend on that from this PR and rebase on main

@Lokathor
Copy link
Owner

It does hint that we may want to rename NoPadding to AllBytesInit, since it's not released yet. I'll leave that call up to you since you've lead the design on this one.

@fu5ha
Copy link
Collaborator Author

fu5ha commented Mar 30, 2022

Hmmmmmmm

@fu5ha
Copy link
Collaborator Author

fu5ha commented Mar 30, 2022

I dunno, I still think NoPadding is the more elegant name here, and still works well, if slightly less precise? The union case seems like a bit of unique side case. I would say we stay with NoPadding for now, and we could re-evaluate when the time for 2.0 comes whether we want to keep it or change.

@JakobDegen
Copy link

Maybe NoUninit?

@fu5ha
Copy link
Collaborator Author

fu5ha commented Mar 30, 2022

I could dig it. What if we merge this and then I make another PR that does the blanket rename?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants