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

Support #[derive(Copy, Clone)] and #[derive(Clone)] for unions with Copy fields #36043

Closed
petrochenkov opened this issue Aug 27, 2016 · 7 comments

Comments

@petrochenkov
Copy link
Contributor

#[derive(Clone)] // OK, both `u8` and `u16` are `Copy`, `clone` is implemented as `memcpy`
union U {
    a: u8,
    b: u16,
}

This wasn't implemented in #36016 for reasons described in #36016 (comment)
cc #32836 #31414 @durka

@durka
Copy link
Contributor

durka commented Aug 27, 2016

I knew that hack would come back to bite us :)

We can just remove it if we agree on the policy that all Clone + !Copy
types will eventually be fixed...

On Sat, Aug 27, 2016 at 12:03 PM, Vadim Petrochenkov <
[email protected]> wrote:

#[derive(Clone)] // OK, both u8 and u16 are Copy, clone is implemented as memcpy
union U {
a: u8,
b: u16,
}

This wasn't implemented in #36016
#36016 for reasons described in #36016
(comment)
#36016 (comment)
cc #32836 #32836 #31414
#31414 @durka
https://github.com/durka


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#36043, or mute the thread
https://github.com/notifications/unsubscribe-auth/AAC3n3LCGRuz0AbI9KLb4xGplwrZxsYFks5qkF-6gaJpZM4JuuYD
.

@durka
Copy link
Contributor

durka commented Aug 27, 2016

Reproducing @petrochenkov's comment from the union PR:

There are some difficulties with the current implementation of #[derive(Clone)], it generates pattern matching even for the "shallow clone" case to check bounds, but these patterns are not appropriate for unions, this needs to be done in some other way somehow in some other PR.

#[derive(Copy, Clone)]
struct S {
    a: u8,
    b: u8,
}

=>

impl ::std::clone::Clone for S {
    #[inline]
    fn clone(&self) -> S {
        match *self {

            S { a: ref __self_0_0, b: ref __self_0_1 } => { // <== BAD

                ::std::clone::assert_receiver_is_clone(&(*__self_0_0));
                ::std::clone::assert_receiver_is_clone(&(*__self_0_1));
                *self
            }
        }
    }
}
impl ::std::marker::Copy for S { }
struct S {
    a: u8,
    b: u8,
}

Can we instead remove the parameter from assert_receiver_is_clone and generate something like this?

// libcore
pub fn assert_receiver_is_clone<T: Clone + ?Sized>() {}

union U { a: u8, b: u16 }

impl ::std::clone::Clone for U {
    #[inline]
    fn clone(&self) -> U {
        ::std::clone::assert_receiver_is_clone::<u8>();
        ::std::clone::assert_receiver_is_clone::<u16>();
        *self
    }
}

@nagisa
Copy link
Member

nagisa commented Aug 27, 2016

For unions derive(Clone) without derive(Copy) makes little sense IMO, since the fields must be Copy to derive either, so we should just require users to derive both at all times for unions.

@Stebalien
Copy link
Contributor

(@durka pedantic !Clone + Copy)

@durka
Copy link
Contributor

durka commented Aug 28, 2016

@Stebalien oops yeah.

On Sat, Aug 27, 2016 at 8:46 PM, Steven Allen [email protected]
wrote:

(@durka https://github.com/durka pedantic !Clone + Copy)


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#36043 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAC3nxS6M-5dEOg8k7g49UKYPvPSi_dXks5qkNpSgaJpZM4JuuYD
.

@petrochenkov
Copy link
Contributor Author

petrochenkov commented Aug 28, 2016

By the way, why do #[derive(Clone, Copy)] and #[derive(Copy, Clone)] generate different code? (Unoptimized in the first case.)

@durka
Copy link
Contributor

durka commented Aug 28, 2016

Uh, they aren't supposed to. I added the check to the #[derive_Clone] handler because I figured even if you wrote #[derive(Clone, Copy)] then all the attributes would be attached before the handlers are called. But I guess I thought wrong? Or maybe the behavior changed?

Manishearth added a commit to Manishearth/rust that referenced this issue Sep 15, 2016
Improve shallow `Clone` deriving

`Copy` unions now support `#[derive(Clone)]`.
Less code is generated for `#[derive(Clone, Copy)]`.
+
Unions now support `#[derive(Eq)]`.
Less code is generated for `#[derive(Eq)]`.

---
Example of code reduction:
```
enum E {
	A { a: u8, b: u16 },
	B { c: [u8; 100] },
}
```
Before:
```
fn clone(&self) -> E {
    match (&*self,) {
        (&E::A { a: ref __self_0, b: ref __self_1 },) => {
            ::std::clone::assert_receiver_is_clone(&(*__self_0));
            ::std::clone::assert_receiver_is_clone(&(*__self_1));
            *self
        }
        (&E::B { c: ref __self_0 },) => {
            ::std::clone::assert_receiver_is_clone(&(*__self_0));
            *self
        }
    }
}
```
After:
```
fn clone(&self) -> E {
    {
        let _: ::std::clone::AssertParamIsClone<u8>;
        let _: ::std::clone::AssertParamIsClone<u16>;
        let _: ::std::clone::AssertParamIsClone<[u8; 100]>;
        *self
    }
}
```

All the matches are removed, bound assertions are more lightweight.
`let _: Checker<CheckMe>;`, unlike `checker(&check_me);`, doesn't have to be translated by rustc_trans and then inlined by LLVM, it doesn't even exist in MIR, this means faster compilation.

---
Union impls are generated like this:
```
union U {
	a: u8,
	b: u16,
	c: [u8; 100],
}
```
```
fn clone(&self) -> U {
    {
        let _: ::std::clone::AssertParamIsCopy<Self>;
        *self
    }
}
```

Fixes rust-lang#36043
cc @durka
r? @alexcrichton
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

No branches or pull requests

4 participants