Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign up#[derive] sometimes uses incorrect bounds #26925
Comments
This comment has been minimized.
This comment has been minimized.
|
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.) |
This comment has been minimized.
This comment has been minimized.
|
#19839 is relevant. |
steveklabnik
added
the
A-syntaxext
label
Jul 20, 2015
This comment has been minimized.
This comment has been minimized.
|
The bounds that 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 |
eefriedman
referenced this issue
Sep 20, 2015
Closed
derive(Default) fails on struct using associated type of param. #28553
This comment has been minimized.
This comment has been minimized.
|
In terms of syntax, it might perhaps be possible to generalize |
This comment has been minimized.
This comment has been minimized.
|
GHC handles the case described in @eefriedman's comment:
It works:
The output is:
Maybe GHC "inlines" the private bounds until it obtains all-public bounds. |
jonas-schievink
referenced this issue
Nov 20, 2015
Closed
Always-true Equality for PhantomData<T> to PhantomData<T> #1375
jonas-schievink
referenced this issue
Feb 9, 2016
Closed
Failing to use derive for nested types parameterized on associated types #31518
Stebalien
referenced this issue
Mar 13, 2016
Closed
Weird Debug trait constraint when using associated type #32216
Stebalien
referenced this issue
Apr 2, 2016
Closed
#[derive(Clone)] always require generic arguments to be Clone, which is too restrictive #32673
huonw
referenced this issue
Apr 12, 2016
Closed
Requires `Copy` constraint on parametrized struct that is already known to #[derive(Copy)] #32872
jonas-schievink
referenced this issue
Aug 3, 2016
Closed
#[derive(Default)] constraints type parameters despite not being required for a Default bound. #35263
petrochenkov
referenced this issue
Sep 22, 2016
Closed
Deriving Clone in structs with unions inside expect type parameters to be Clone, unlike structs. #36640
brson
added
T-lang
P-low
labels
Apr 4, 2017
This was referenced Apr 25, 2017
ebkalderon
referenced this issue
May 3, 2017
Merged
Derive convenience traits for PSO components and a few other types #1249
This comment has been minimized.
This comment has been minimized.
Mark-Simulacrum
changed the title
#[derive] is too conservative with field trait bounds
#[derive] sometimes uses incorrect bounds
May 7, 2017
This comment has been minimized.
This comment has been minimized.
|
(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 #[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>; |
This comment has been minimized.
This comment has been minimized.
|
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. |
This comment has been minimized.
This comment has been minimized.
|
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. |
Mark-Simulacrum
referenced this issue
May 16, 2017
Closed
rustc doesn't correctly derive Copy/Clone in the presence of associated type members #23575
oli-obk
referenced this issue
Jun 19, 2017
Open
Disable `expl_impl_clone_on_copy` when `Copy` is manually implemented. #1254
jonas-schievink
referenced this issue
May 29, 2018
Closed
Strange issue with parametized types #51164
This comment has been minimized.
This comment has been minimized.
gavofyork
commented
Jun 3, 2018
I couldn't have put is better myself. For the record, this bug (Yes, bug. Not feature-request as it is erroneously tagged.) is causing substantial headaches in the Parity/Polkadot codebase. A workaround would be much appreciated. |
This comment has been minimized.
This comment has been minimized.
|
@gavofyork Note my previous comment, #26925 (comment) - at least part of the problem require advanced typesystem (more accurately, "typeclass/trait system") features for sound solutions. |
This comment has been minimized.
This comment has been minimized.
|
As a stopgap measure, it might make sense to make an attribute like I use here: https://github.com/remexre/display_attr/blob/master/tests/enums.rs#L11 to allows the user to specify bounds; it'd take a bit of time to make all of the derives, but it's a solution. |
This comment has been minimized.
This comment has been minimized.
|
The workaround is a crate that lets the user specify the correct bounds, such as derivative which was linked from this thread before. Hyperbolic comments saying "this is so easy, just fix it" aren't constructive. |
This comment has been minimized.
This comment has been minimized.
gavofyork
commented
Jun 4, 2018
|
@durka @remen In this case, I'm stuck deriving not just the usual I appreciate that a "perfect" derive may yet be some time away. However, some provision to programatically remove this erroneous requirement on a case-by-case basis would alleviate so much of the associated pain, and unless I'm missing something, would be perfectly safe to introduce. |
This comment has been minimized.
This comment has been minimized.
This issue will not fix Serde's derives, which are provided by Serde itself. I don't know if that project would be willing to accept such a patch, but you could speak with the maintainers. |
This comment has been minimized.
This comment has been minimized.
|
@gavofyork Well, rustc doesn't determine how Serialize decides to handle generating generic bounds on its own Derives. EDIT: @shepmaster Jinx! |
This comment has been minimized.
This comment has been minimized.
|
Serde actually already has the override available. |
This comment has been minimized.
This comment has been minimized.
|
That's a pretty hyperbolic and unconstructive comment. This is a rarely
encountered problem and there are workarounds. There's a lot of people
subscribed here so let's keep comments to a minimum unless there's
something new to say, shall we?
…On Tue, Jun 26, 2018, 07:20 A1-Triard ***@***.***> wrote:
It looks very strange: an important part of the Rust (traits deriving) is
broken up to fully unusable state and there is no any solution for three
year.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#26925 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAC3n8SYoUInm-v4fpU8yIZ76doguGLQks5uAhj8gaJpZM4FVpdB>
.
|
pmarcelll
referenced this issue
Jul 5, 2018
Closed
Derivable traits do not work for `PhantomData<T>` if `T` does not impl them as well #52079
This comment has been minimized.
This comment has been minimized.
ramosbugs
commented
Jul 16, 2018
|
It looks like this issue affects the failure_derive crate as well. In the following code fragment,
The macro expansion ends up being something like:
In this case, the parameterized type |
This comment has been minimized.
This comment has been minimized.
As stated above, crates can create their own mechanism to fine-tune their own derive bounds. Serde is an example of such a crate. You should file an issue or submit a PR to failure. |
ramosbugs
referenced this issue
Jul 16, 2018
Closed
Allow using generic type parameters that don't implement `Fail` #186
added a commit
to tene/text-ui
that referenced
this issue
Aug 19, 2018
This comment has been minimized.
This comment has been minimized.
|
I'm gonna leave a reminder here for @nikomatsakis to read https://arxiv.org/abs/1608.05233 (if he hasn't already) to see if it'd help rustc/Chalk in any way support the "coinductive" case. |
This comment has been minimized.
This comment has been minimized.
|
@nikomatsakis why the obsession with solving recursive type bounds in order to make As I understand it,
Macros can cover the #[derive(Clone)]
struct Foo<T: Iterator + ?Sized> {
data: T::Item
}
// generates:
impl<T> Clone for Foo<T>
where
T: Iterator + ?Sized, // copied from struct requirements
typeof(Self::data): Clone
{
fn clone(&self) -> Self {
Foo { data: self.data.clone() }
}
}
Note that this isn't just about making |
This comment has been minimized.
This comment has been minimized.
|
The problem @dhardy is this. Assume that impl<T> Clone for Foo<T> where Foo<T>: Clone { ... }This cyclic impl will never terminate: in order to "use" the impl, you need to provide the impl itself. In rustc today, this results in an overflow error, as you can see from this example: struct Tree<T> {
data: T,
children: Vec<Tree<T>>,
}
impl<T> Clone for Tree<T>
where
T: Clone,
Vec<Tree<T>>: Clone,
{
fn clone(&self) -> Self {
panic!()
}
}
fn is_clone<T: Clone>() {}
fn main() {
is_clone::<Tree<u32>>();
}
Once we transition to Chalk, it will not overflow, but it simply error: there is no As an interim step, I think we should just add some annotations that let you "opt-out" of the additional bounds when deriving for specific type parameters. This seems like a fine thing to do no matter what. Also, i'm not 100% confident that we want to adopt the "perfect" design here. It marks a subtle change in the semver policy that is not obviously good. Today, if I have Assuming we did want to adopt the perfect path, we need to define how it gets lowered to the underlying logical rules. Keeping in mind that derive is a "dumb macro", it seems like we really ought to move "the smarts" there into trait solving, so that e.g. the simple impls I gave above would be accepted. One way to do this is to adopt a "co-inductive semantics" for trait matching, but that carries other complications if we are not careful (and may well invalidate unsafe code, for example, that relies on today's inductive behavior to rule out cycles). There may be other solutions, though. That said, I do have the feeling that one ought to be able to have a coinductive-style semantics for cases like UPDATE: Updated the example to be more realistic. |
This comment has been minimized.
This comment has been minimized.
|
Thanks @nikomatsakis for the explanation.
And yet if the bound is removed,
So this is the crux of the issue. One can have fun with toy examples: trait Trait {}
impl<T: Trait> Trait for T {}This currently causes an overflow error when trying to check the bound, but fundamentally it's harmless: trait Trait {
fn foo(&self);
}
impl<T: Trait> Trait for T {
fn foo(&self) {
self.foo()
}
}... now we have "bad" code (infinite recursion). As @comex points out, Rust freely allows diverging code, so technically we don't need to solve this in the type system. Rust also already has warnings about infinite function recursion; quite possibly the same logic could be used to warn about this case. So I don't see any real reason for the bound checker to reject this code.
I saw this argument above, but it seemed like a strange one. It is a minor issue that Note for readers: of course this implies that the solution I proposed above, bounds on |
comex commentedJul 9, 2015
In the following code:
both derives expand to impls that require the corresponding trait to be implemented on the type parameter, e.g.:
However, this isn't actually necessary, as
Y<T>will still be eligible forCopyregardless of whetherTis.This may be hard to fix, given the compilation phase at which
#[derive]works...