-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Add associated types support for #[derive(...)] #21237
Conversation
(rust_highfive has picked a reviewer for you, use r? to override) |
a861370
to
46a221e
Compare
Unfortunately this can't land quite yet. It turns out the visibility checker reports that this is exposing a private type: struct Foo<T>(T);
impl<T> Clone for Foo<T> where T: Clone { ... }
pub struct Bar<T>(Foo<T>);
impl<T> Clone for Bar<T> where Foo<T>: Clone { ... } @nikomatsakis said in #21203 (comment) that the privacy rules are being too strict here and might need to be relaxed. |
r? @huonw - you're more familiar with |
I'm not so sure about the privacy thing anymore. |
|
||
impl<'a> visit::Visitor<'a> for Visitor<'a> { | ||
fn visit_path(&mut self, path: &'a ast::Path, _id: ast::NodeId) { | ||
match path.segments.first() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suspect this should check that path.segments
has length 1, or maybe the shadowing rules means it doesn't matter? (i.e. is struct Foo<std> { x: std::option::Option<u8> }
legal?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Drat, you're right, this currently compiles:
mod T {
pub type Type = i32;
}
trait Trait {
type Type;
}
struct Baz<T: Trait> {
a: T::Type,
}
I filed #21301 to get that fixed. It'll probably not be that much of an issue in practice though, as you'd have to be crazy to write that :)
@erickt what's the status of this? Is
still true? |
@huonw: Ack! I was on a business trip this past week and forgot all about wrapping up this PR. I'll try to get it done this week :) |
46a221e
to
85ba662
Compare
@huonw: Should work now, and it passes locally. re-r? |
85ba662
to
6311d3b
Compare
This approach is definitely an improvement, but it doesn't solve #7671 completely, e.g. #[derive(Debug)] // T: Debug is not inserted
struct Foo<T>;
#[derive(Debug)] // T: Debug is inserted, but T is still phantom
struct Bar<T> {
x: Foo<T>
} Also, I think this may "break" the standard library marker types like That said, I'm not sure that it is necessary to have these restricted implementions. |
I suspect it's not worth trying to solve the general case of #7671. The privacy thing is a real kicker - if a public type |
After talking to @huonw, I feel like there isn't a great solution that works for phantom/marker types at the same time as working for an edge case of associated types. Say we extend @huonw's phantom type example from before, this time adding an associated type that's not using the trait type: #[derive(Debug)]
struct Foo<T>;
#[derive(Debug)]
struct Bar<T>(Foo<T>);
trait Trait { type Type; }
#[derive(Debug)]
struct Baz<T: Trait>(T::Type); We can either generate an impl that constrains all the type parameters, which is our current approach: struct Foo<T>;
impl<T> Debug for Foo<T> where T: Debug { ... }
struct Bar<T>(Foo<T>);
impl<T> Debug for Bar<T> where T: Debug { ... }
trait Trait { type Type; }
struct Baz<T: Trait>(T::Type);
impl<T> Debug for Baz<T> where T: Debug, T::Type: Debug { ... } Notice that the impl for struct Foo<T>;
impl<T> Debug for Foo<T> { ... }
struct Bar<T>(Foo<T>);
impl<T> Debug for Bar<T> where T: Debug { ... }
trait Trait { type Type; }
struct Baz<T: Trait>(T::Type);
impl<T> Debug for Baz<T> where T::Type: Debug { ... } Notice that for Does anyone have an opinion on which approach to use, or if there are some good heuristics here to decide what to do here? |
None => {} | ||
} | ||
} | ||
ast::TyQPath(_) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, this will pick up non-generic QPath's too, right? e.g. struct Foo(<Bar as Iterator>::Item);
.
(I guess these come up rarely enough that it probably doesn't matter?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll add a pass through the QPath that checks if it references any of the type parameters. If it does, I'll add it to the types to be constrained. If not, i'll skip it. That should protect against this case.
6311d3b
to
edb33f2
Compare
Took a while, but I finally updated the patch to compile on head and address @goffrie's comment. |
@@ -558,6 +652,21 @@ impl<'a> TraitDef<'a> { | |||
enum_def: &EnumDef, | |||
type_ident: Ident, | |||
generics: &Generics) -> P<ast::Item> { | |||
let mut field_tys = Vec::new(); | |||
|
|||
for variant in enum_def.variants.iter() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this be written as
let field_tys = enum_def.variants.flat_map(|variant| {
match variant.node.kind {
ast::VariantKind::TupleVariantKind(ref args) => {
args.iter().map(|arg| arg.ty.clone())
}
ast::VariantKind::StructVariantKind(ref args) => {
args.fields.iter().map(|field| field.node.ty.clone())
}
}
}).collect();
❓
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tamird: Unfortunately it cannot because those inner iterators are actually two different types.
@bors: r+ edb33f2 |
⌛ Testing commit edb33f2 with merge e989552... |
💔 Test failed - auto-mac-64-opt |
|
⌛ Testing commit 7971f39 with merge 4c3f968... |
💔 Test failed - auto-linux-64-x-android-t |
7971f39
to
9cabe27
Compare
@bors: retry |
@bors: r+ rollup |
📌 Commit 9cabe27 has been approved by |
⌛ Testing commit 9cabe27 with merge 72d0695... |
💔 Test failed - auto-win-32-opt |
@bors: retry |
This PR adds support for associated types to the `#[derive(...)]` syntax extension. In order to do this, it switches over to using where predicates to apply the type constraints. So now this: ```rust type Trait { type Type; } #[derive(Clone)] struct Foo<A> where A: Trait { a: A, b: <A as Trait>::Type, } ``` Gets expended into this impl: ```rust impl<A: Clone> Clone for Foo<A> where A: Trait, <A as Trait>::Type: Clone, { fn clone(&self) -> Foo<T> { Foo { a: self.a.clone(), b: self.b.clone(), } } } ```
This PR adds support for associated types to the `#[derive(...)]` syntax extension. In order to do this, it switches over to using where predicates to apply the type constraints. So now this: ```rust type Trait { type Type; } #[derive(Clone)] struct Foo<A> where A: Trait { a: A, b: <A as Trait>::Type, } ``` Gets expended into this impl: ```rust impl<A: Clone> Clone for Foo<A> where A: Trait, <A as Trait>::Type: Clone, { fn clone(&self) -> Foo<T> { Foo { a: self.a.clone(), b: self.b.clone(), } } } ```
This PR adds support for associated types to the `#[derive(...)]` syntax extension. In order to do this, it switches over to using where predicates to apply the type constraints. So now this: ```rust type Trait { type Type; } #[derive(Clone)] struct Foo<A> where A: Trait { a: A, b: <A as Trait>::Type, } ``` Gets expended into this impl: ```rust impl<A: Clone> Clone for Foo<A> where A: Trait, <A as Trait>::Type: Clone, { fn clone(&self) -> Foo<T> { Foo { a: self.a.clone(), b: self.b.clone(), } } } ```
This PR adds support for associated types to the `#[derive(...)]` syntax extension. In order to do this, it switches over to using where predicates to apply the type constraints. So now this: ```rust type Trait { type Type; } #[derive(Clone)] struct Foo<A> where A: Trait { a: A, b: <A as Trait>::Type, } ``` Gets expended into this impl: ```rust impl<A: Clone> Clone for Foo<A> where A: Trait, <A as Trait>::Type: Clone, { fn clone(&self) -> Foo<T> { Foo { a: self.a.clone(), b: self.b.clone(), } } } ```
derive applies the necessary trait bounds to derived impl. see rust-lang/rust#21237 for more details
435: derive Eq for other core geo types r=frewsxcv a=michaelkirk Adds `Eq` conformance to other core geo types, building off of #431 > Seems reasonable to me! Yeah +1 to adding the same impl for all the other core geo types Rather than spelling out the impl, I've taken advantage of how `derive` already bounds the generated impl for us. See rust-lang/rust#21237 for more details I wrote a few doc-tests while developing, but I reverted them in fc33f9f since they're pretty verbose, and seem superfluous since they're only asserting rust language semantics rather than anything substantial in this lib. I can un-revert them if you'd prefer. Co-authored-by: Michael Kirk <[email protected]>
I think this is badly documented. A few sentences at https://doc.rust-lang.org/std/clone/trait.Clone.html#derivable would be nice. |
This PR adds support for associated types to the
#[derive(...)]
syntax extension. In order to do this, it switches over to using where predicates to apply the type constraints. So now this:Gets expended into this impl: