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 #26925

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

#[derive] sometimes uses incorrect bounds #26925

comex opened this issue Jul 9, 2015 · 75 comments
Labels
A-proc-macros Area: Procedural macros A-traits 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.

@jhpratt
Copy link
Member

jhpratt commented Apr 13, 2023

It is related to the issue in a way, just not in any manner that can be generalized. The only thing that changed was #[derive(Default)] on enums, as it was explicitly accepted.

For what it's worth, the disagreement on bounds is the sole reason that #[default] can't be placed on an enum variant with data.

@ggutoski
Copy link

Yet another example from associated types: Playground

ids1024 added a commit to ids1024/smithay that referenced this issue Jul 12, 2023
These are still needed to implemented `Debug` without a `D: Debug`
bound.

If there were *one* manual debug impl like this I'd add a comment noting
that it is the same as the derived implementation but with a different
bound, and link to rust-lang/rust#26925. But
adding that comment everywhere would be verbose...

This still slightly simplifies many of these implementations, since they
used to require `Debug` bounds for `::KeyboardFocus`, `::PointerFocus`
etc. When removing those I switched to deriving `Debug` since that
*seemed* to work.
@Dylan-DPC Dylan-DPC added C-bug Category: This is a bug. and removed C-feature-request Category: A feature request, i.e: not implemented / a PR. labels Nov 26, 2023
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.
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-traits 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