Skip to content

Add associated types support for #[derive(...)] - #21237

Merged
bors merged 2 commits into
rust-lang:masterfrom
erickt:derive-assoc-types
Mar 26, 2015
Merged

Add associated types support for #[derive(...)]#21237
bors merged 2 commits into
rust-lang:masterfrom
erickt:derive-assoc-types

Conversation

@erickt

Copy link
Copy Markdown
Contributor

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:

typeTrait{typeType;}#[derive(Clone)]structFoo<A>whereA:Trait{a:A,b: <AasTrait>::Type,}

Gets expended into this impl:

impl<A:Clone>CloneforFoo<A>whereA:Trait,
<AasTrait>::Type:Clone,{fnclone(&self) -> Foo<T>{Foo{a:self.a.clone(),b:self.b.clone(),}}}

@rust-highfive

Copy link
Copy Markdown
Contributor

r? @alexcrichton

(rust_highfive has picked a reviewer for you, use r? to override)

@erickt

Copy link
Copy Markdown
ContributorAuthor

Unfortunately this can't land quite yet. It turns out the visibility checker reports that this is exposing a private type:

structFoo<T>(T);impl<T>CloneforFoo<T>whereT:Clone{ ... }pubstructBar<T>(Foo<T>);impl<T>CloneforBar<T>whereFoo<T>:Clone{ ... }

@nikomatsakis said in #21203 (comment) that the privacy rules are being too strict here and might need to be relaxed.

@alexcrichton

Copy link
Copy Markdown
Member

r? @huonw - you're more familiar with #[derive] internals than I

@nikomatsakis

Copy link
Copy Markdown
Contributor

I'm not so sure about the privacy thing anymore.

Copy link
Copy Markdown
Contributor

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?)

Copy link
Copy Markdown
ContributorAuthor

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:

modT{pubtypeType = i32;}traitTrait{typeType;}structBaz<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 :)

@huonw

huonw commented Feb 1, 2015

Copy link
Copy Markdown
Contributor

@erickt what's the status of this? Is

Unfortunately this can't land quite yet

still true?

@erickt

Copy link
Copy Markdown
ContributorAuthor

@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 :)

@erickt

Copy link
Copy Markdown
ContributorAuthor

@huonw: Should work now, and it passes locally. re-r?

@erickt

Copy link
Copy Markdown
ContributorAuthor

@huonw: updated the PR to also work with phantom types, closing #7671.

@huonw

huonw commented Feb 8, 2015

Copy link
Copy Markdown
Contributor

This approach is definitely an improvement, but it doesn't solve #7671 completely, e.g.

#[derive(Debug)]// T: Debug is not insertedstructFoo<T>;#[derive(Debug)]// T: Debug is inserted, but T is still phantomstructBar<T>{x:Foo<T>}

Also, I think this may "break" the standard library marker types like InvariantType (and Phantom if/when it appears) which are possibly meant to represent that some type behaves like it stores a T, so the relevant traits should only be implemented when T has them implemented. The type parameters to these types are phantom types so #[derive] will no longer insert those bounds after this patch.

That said, I'm not sure that it is necessary to have these restricted implementions.

@goffrie

Copy link
Copy Markdown
Contributor

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 Bar<T> contains a private type Foo<T>, then Bar essentially needs to expose the full rules for Foo<T> being Clone-able - so the derive mechanism would need to see what impls exist for Foo<T>, which might depend on even more private types... As a middle ground, maybe we could emit the where Foo<T>: Clone clause when Foo is public (or Bar is private), but use the approximation where T: Clone otherwise?

@erickt

Copy link
Copy Markdown
ContributorAuthor

@goffrie: unfortunately in libsyntax at this point in time we can't see if a type is public or private. I believe we would need typeck in order to have that information.

@huon: good point. Maybe phantom types impls should written by hand.

@erickt

Copy link
Copy Markdown
ContributorAuthor

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)]structFoo<T>;#[derive(Debug)]structBar<T>(Foo<T>);traitTrait{typeType;}#[derive(Debug)]structBaz<T:Trait>(T::Type);

We can either generate an impl that constrains all the type parameters, which is our current approach:

structFoo<T>;impl<T>DebugforFoo<T>whereT:Debug{ ... }structBar<T>(Foo<T>);impl<T>DebugforBar<T>whereT:Debug{ ... }traitTrait{typeType;}structBaz<T:Trait>(T::Type);impl<T>DebugforBaz<T>whereT:Debug,T::Type:Debug{ ... }

Notice that the impl for Baz has an unnecessary T: Debug constraint. Or only the type parameters used in the field:

structFoo<T>;impl<T>DebugforFoo<T>{ ... }structBar<T>(Foo<T>);impl<T>DebugforBar<T>whereT:Debug{ ... }traitTrait{typeType;}structBaz<T:Trait>(T::Type);impl<T>DebugforBaz<T>whereT::Type:Debug{ ... }

Notice that for Bar there is an unnecessary T: Debug constraint because Foo<T> doesn't use T.

Does anyone have an opinion on which approach to use, or if there are some good heuristics here to decide what to do here?

Copy link
Copy Markdown
Contributor

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?)

Copy link
Copy Markdown
ContributorAuthor

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.

@erickt
ericktforce-pushed the derive-assoc-types branch from 6311d3b to edb33f2CompareMarch 14, 2015 07:43
@erickt

Copy link
Copy Markdown
ContributorAuthor

Took a while, but I finally updated the patch to compile on head and address @goffrie's comment.

Copy link
Copy Markdown
Contributor

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();

Copy link
Copy Markdown
ContributorAuthor

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.

@erickt

Copy link
Copy Markdown
ContributorAuthor

@bors: r+ edb33f2

@bors

bors commented Mar 19, 2015

Copy link
Copy Markdown
Collaborator

⌛ Testing commit edb33f2 with merge e989552...

@bors

bors commented Mar 19, 2015

Copy link
Copy Markdown
Collaborator

💔 Test failed - auto-mac-64-opt

@Manishearth

Copy link
Copy Markdown
Member

---- [compile-fail] compile-fail/derive-assoc-type-not-impl.rs stdout ----
error: compile-fail test compiled successfully!
status: exit code: 0
command: x86_64-apple-darwin/stage2/bin/rustc /Users/rustbuild/src/rust-buildbot/slave/auto-mac-64-opt/build/src/test/compile-fail/derive-assoc-type-not-impl.rs -L x86_64-apple-darwin/test/compile-fail/ --target=x86_64-apple-darwin -L x86_64-apple-darwin/test/compile-fail/derive-assoc-type-not-impl.stage2-x86_64-apple-darwinlibaux -C prefer-dynamic -o x86_64-apple-darwin/test/compile-fail/derive-assoc-type-not-impl.stage2-x86_64-apple-darwin --cfg rtopt --cfg debug -O -L x86_64-apple-darwin/rt
stdout:
------------------------------------------
------------------------------------------
stderr:
------------------------------------------
------------------------------------------
thread '[compile-fail] compile-fail/derive-assoc-type-not-impl.rs' panicked at 'explicit panic', /Users/rustbuild/src/rust-buildbot/slave/auto-mac-64-opt/build/src/compiletest/runtest.rs:1482
failures:
[compile-fail] compile-fail/derive-assoc-type-not-impl.rs

@bors

bors commented Mar 24, 2015

Copy link
Copy Markdown
Collaborator

⌛ Testing commit 7971f39 with merge 4c3f968...

@bors

bors commented Mar 24, 2015

Copy link
Copy Markdown
Collaborator

💔 Test failed - auto-linux-64-x-android-t

@erickt
ericktforce-pushed the derive-assoc-types branch from 7971f39 to 9cabe27CompareMarch 24, 2015 21:43
@erickt

Copy link
Copy Markdown
ContributorAuthor

@bors: retry

@erickt

Copy link
Copy Markdown
ContributorAuthor

@bors: r+ rollup

@bors

bors commented Mar 25, 2015

Copy link
Copy Markdown
Collaborator

📌 Commit 9cabe27 has been approved by erickt

@bors

bors commented Mar 25, 2015

Copy link
Copy Markdown
Collaborator

⌛ Testing commit 9cabe27 with merge 72d0695...

@bors

bors commented Mar 25, 2015

Copy link
Copy Markdown
Collaborator

💔 Test failed - auto-win-32-opt

@erickt

Copy link
Copy Markdown
ContributorAuthor

@bors: retry

Manishearth added a commit to Manishearth/rust that referenced this pull request Mar 26, 2015
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(),
}
}
}
```
erickt added a commit to erickt/rust that referenced this pull request Mar 26, 2015
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(),
}
}
}
```
@erickterickt mentioned this pull request Mar 26, 2015
erickt added a commit to erickt/rust that referenced this pull request Mar 26, 2015
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(),
}
}
}
```
@erickterickt mentioned this pull request Mar 26, 2015
@bors

bors commented Mar 26, 2015

Copy link
Copy Markdown
Collaborator

⌛ Testing commit 9cabe27 with merge 557d434...

bors added a commit that referenced this pull request Mar 26, 2015
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(),
}
}
}
```
@bors

bors commented Mar 26, 2015

Copy link
Copy Markdown
Collaborator

@bors
bors merged commit 9cabe27 into rust-lang:masterMar 26, 2015
michaelkirk added a commit to michaelkirk/geo that referenced this pull request Apr 1, 2020
derive applies the necessary trait bounds to derived impl.
see rust-lang/rust#21237 for more details
borsBot added a commit to georust/geo that referenced this pull request Apr 2, 2020
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 <michael.code@endoftheworl.de>
@jendrikw

Copy link
Copy Markdown
Contributor

I think this is badly documented. A few sentences at https://doc.rust-lang.org/std/clone/trait.Clone.html#derivable would be nice.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants

@erickt@rust-highfive@alexcrichton@nikomatsakis@huonw@goffrie@bors@Manishearth@jendrikw@tamird