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

#[derive] sometimes uses incorrect bounds (aka lack of "perfect derive") #26925

Open
comex opened this issue Jul 9, 2015 · 79 comments
Open

#[derive] sometimes uses incorrect bounds (aka lack of "perfect derive") #26925

comex opened this issue Jul 9, 2015 · 79 comments
Labels
A-proc-macros Area: Procedural macros A-trait-system Area: Trait system C-bug Category: This is a bug. P-low Low priority T-lang Relevant to the language team, which will review and decide on the PR/issue.

Comments

@comex
Copy link
Contributor

comex commented Jul 9, 2015

In the following code:

#[derive(Copy, Clone)]
struct Y<T>(&'static fn(T));

both derives expand to impls that require the corresponding trait to be implemented on the type parameter, e.g.:

#[automatically_derived]
impl<T: ::std::marker::Copy> ::std::marker::Copy for Y<T>
where
     T: ::std::marker::Copy
{}

However, this isn't actually necessary, as Y<T> will still be eligible for Copy regardless of whether T is.

This may be hard to fix, given the compilation phase at which #[derive] works...

@huonw
Copy link
Member

huonw commented Jul 9, 2015

This is a dupe of #7671 which I suspect may've been accidentally closed. (i.e. the patch that closed it was modified to no longer tackle it.)

@apasel422
Copy link
Contributor

#19839 is relevant.

@steveklabnik steveklabnik added the A-syntaxext Area: Syntax extensions label Jul 20, 2015
@eefriedman
Copy link
Contributor

The bounds that #[derive] uses aren't conservative, they're just wrong. Whether a type parameter implements the derived trait is in theory unrelated to whether a given field implements that trait. The defaults just appear to work most of the time because in practice people use traits and field types where the approximation is reasonably accurate.

Unfortunately, the obvious fix (just bounding the field types) causes breakage:

#[derive(Clone)]
struct Item<A> { inner: A }
#[derive(Clone)]
pub struct IntoIter<A> { inner: Item<A> }
// The obvious bound is `Item<A> : Clone`, but we can't use that bound
// because `Item` is private.

And actually, in the general case there is no bound we can expose because it could involve a requirement that a parameter type implements a private trait.

We could in theory special-case certain types syntactically, but that only improves a few special cases like Clone of a reference.

It's possible that we could punt computing the bounds off to type-checking (add syntax like where each field : Clone), and come up with a type-sensitive heuristic; that's probably a lot of work, though. We could also extend the #[derive] syntax to allow the user to specify the correct bounds; easy to implement, but not very intuitive to use.

@Yoric
Copy link
Contributor

Yoric commented Oct 13, 2015

In terms of syntax, it might perhaps be possible to generalize ? and let devs specify where T: ?Clone. Not sure how the compiler would interpret this post-derive, though.

@jorendorff
Copy link
Contributor

GHC handles the case described in @eefriedman's comment:

module DeriveShowPrivate(IntoIter(IntoIter), f) where
data Item a = Item { itemInner :: a } deriving Show
data IntoIter a = IntoIter { inner :: Item a } deriving Show
f v = IntoIter {inner = Item {itemInner = v}}

It works:

import DeriveShowPrivate(f)
main = putStrLn (show (f 37))

The output is:

IntoIter {inner = Item {itemInner = 37}}

Maybe GHC "inlines" the private bounds until it obtains all-public bounds.

@brson brson added T-lang Relevant to the language team, which will review and decide on the PR/issue. P-low Low priority labels Apr 4, 2017
@Mark-Simulacrum
Copy link
Member

There are also cases where derive is more conservative than it should be, see #32872 and #31518.

@Mark-Simulacrum Mark-Simulacrum changed the title #[derive] is too conservative with field trait bounds #[derive] sometimes uses incorrect bounds May 7, 2017
@bluss
Copy link
Member

bluss commented May 7, 2017

(Some contrivance ahead.)

If the bounds would be dropped when possible for existing code, the following would lose its good and intended meaning. The effect would be that RefMut<'a, T> were suddenly Copy, which is something that we imagine is not desirable, even a memory error.

#[derive(Copy, Clone)]
pub struct Ptr<Mark>(*mut u8, PhantomData<Mark>);

pub type Ref<'a, T: 'a> = Ptr<&'a T>;
pub type RefMut<'a, T: 'a> = Ptr<&'a mut T>;

@bluss
Copy link
Member

bluss commented May 7, 2017

With that I think that even if the bounds are plain wrong, they are part of the stock recipe that one can conjure with derive, it's “part of what you order” and we can't change that backwards incompatibly.

Derive can however grow bells and whistles, like the bound customization that rust-derivative and serde offer.

@comex
Copy link
Contributor Author

comex commented May 8, 2017

If there's a good way to make derive do the right thing, the backwards incompatibility issue sounds like the kind of thing epochs could handle.

matthiasgoergens added a commit to 0xmozak/plonky2 that referenced this issue Apr 5, 2024
We can't use Rust's default deriving mechanism, until
rust-lang/rust#26925 and
rust-lang/rust#7671 are fixed.

But fortunately, there's a crate for that.
matthiasgoergens added a commit to 0xmozak/plonky2 that referenced this issue Apr 5, 2024
We can't use Rust's default deriving mechanism, until
rust-lang/rust#26925 and
rust-lang/rust#7671 are fixed.

But fortunately, there's a crate for that.
matthiasgoergens added a commit to 0xmozak/plonky2 that referenced this issue Apr 5, 2024
We can't use Rust's default deriving mechanism, until
rust-lang/rust#26925 and
rust-lang/rust#7671 are fixed.

But fortunately, there's a crate for that.
mathstuf added a commit to mathstuf/rust-derive-builder that referenced this issue Jun 27, 2024
This attribute suppresses the default-generated `derive(Clone)`. This
may be necessary where `derive(Clone)` copies constraints in an
unsatisfiable way.

See: https://smallcultfollowing.com/babysteps//blog/2022/04/12/implied-bounds-and-perfect-derive/
See: rust-lang/rust#26925
Fixes: colin-kiegel#325
mathstuf added a commit to mathstuf/rust-derive-builder that referenced this issue Jun 27, 2024
This attribute suppresses the default-generated `derive(Clone)`. This
may be necessary where `derive(Clone)` copies constraints in an
unsatisfiable way.

See: https://smallcultfollowing.com/babysteps//blog/2022/04/12/implied-bounds-and-perfect-derive/
See: rust-lang/rust#26925
Fixes: colin-kiegel#325
mathstuf added a commit to mathstuf/rust-derive-builder that referenced this issue Jun 27, 2024
This attribute suppresses the default-generated `derive(Clone)`. This
may be necessary where `derive(Clone)` copies constraints in an
unsatisfiable way.

See: https://smallcultfollowing.com/babysteps//blog/2022/04/12/implied-bounds-and-perfect-derive/
See: rust-lang/rust#26925
Fixes: colin-kiegel#325
mathstuf added a commit to mathstuf/rust-derive-builder that referenced this issue Jun 27, 2024
This attribute suppresses the default-generated `derive(Clone)`. This
may be necessary where `derive(Clone)` copies constraints in an
unsatisfiable way.

See: https://smallcultfollowing.com/babysteps//blog/2022/04/12/implied-bounds-and-perfect-derive/
See: rust-lang/rust#26925
Fixes: colin-kiegel#325
mathstuf added a commit to mathstuf/rust-derive-builder that referenced this issue Jun 27, 2024
This attribute suppresses the default-generated `derive(Clone)`. This
may be necessary where `derive(Clone)` copies constraints in an
unsatisfiable way.

See: https://smallcultfollowing.com/babysteps//blog/2022/04/12/implied-bounds-and-perfect-derive/
See: rust-lang/rust#26925
Fixes: colin-kiegel#325
mathstuf added a commit to mathstuf/rust-derive-builder that referenced this issue Jun 27, 2024
This attribute suppresses the default-generated `derive(Clone)`. This
may be necessary where `derive(Clone)` copies constraints in an
unsatisfiable way.

See: https://smallcultfollowing.com/babysteps//blog/2022/04/12/implied-bounds-and-perfect-derive/
See: rust-lang/rust#26925
Fixes: colin-kiegel#325
@la10736
Copy link
Contributor

la10736 commented Oct 10, 2024

Just for reiterate it for the new comers rust-derivative can work around for this issue.

@teohhanhui
Copy link

@la10736:

Just for reiterate it for the new comers rust-derivative can work around for this issue.

mcarton/rust-derivative#117

@la10736
Copy link
Contributor

la10736 commented Oct 10, 2024

Just for reiterate it for the new comers rust-derivative can work around for this issue.

Sorry rust-derivative is not still maintained. But it's a useful starting point of how to find replacement and how to work around it.

@dspyz-matician
Copy link

I use the derive-where crate which seems to have all the functionality I expect 99.9% of the time (the other 0.1% I write manual impls)

robertknight added a commit to robertknight/rten that referenced this issue Oct 11, 2024
Replace derived `Clone` impls for tensor iterators with manual implementations
to remove the unnecessary `T: Clone` bound that the derived impl adds.

See also rust-lang/rust#26925.
@fmease fmease changed the title #[derive] sometimes uses incorrect bounds #[derive] sometimes uses incorrect bounds (aka lack of "perfect derive") Dec 21, 2024
@fmease fmease marked this as a duplicate of #50729 Dec 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-proc-macros Area: Procedural macros A-trait-system Area: Trait system C-bug Category: This is a bug. P-low Low priority T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests