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

Support Self in struct expressions and patterns #37035

Merged
merged 3 commits into from
Oct 28, 2016

Conversation

petrochenkov
Copy link
Contributor

@petrochenkov petrochenkov commented Oct 8, 2016

Struct expressions and patterns generally support type aliases Alias { field: 10 } i.e. they already have to work with ty::Ty to do their job. Self is a type alias (when it's not a type parameter) => struct expressions and patterns should support Self.

Typical example:

impl MyStruct {
    fn new() -> Self {
        Self { a: 10, b: "Hello" }
    }
}

The first commit does some preparations and cleanups, see the commit message for details.
This also fixes couple of bugs related to aliases in struct paths (fixes #36286).

EDIT:
Since struct expressions and patterns always work with ty::Ty now, associated paths in them are also supported. If associated type A::B successfully resolves to a struct (or union) type, then A::B { /* fields */ } is a valid expression/pattern. This will become more important when enum variants are treated as associated items.

r? @eddyb

Copy link
Member

@eddyb eddyb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, certainly something I'd like to use myself.

cc @rust-lang/lang Any objections?

@@ -802,6 +802,34 @@ impl<'tcx> fmt::Display for ty::TraitRef<'tcx> {
}
}

impl<'tcx> ty::TypeVariants<'tcx> {
pub fn descr(&self) -> &'static str {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we have something like this already?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not entirely sure, I've searched a bit, but found only type pretty printing (in the same ppaux.rs), but no short descriptions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if ty.is_fn() {
// Tuple variants have fn type even in type namespace,
// extract true variant type from it.
ty = tcx.no_late_bound_regions(&ty.fn_ret()).unwrap();
Copy link
Contributor Author

@petrochenkov petrochenkov Oct 8, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, this is no longer necessary, need to remove.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Due to the "actual variant definition" vs "variant constructor" split?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, due to tcx.parent_def_id(did) a few lines above.
Only type arguments are taken from the variant (from base_segments.last()), ast_path_to_ty works with the enum after that and returns enum's type, which cannot be a function.

@Ericson2314
Copy link
Contributor

Ericson2314 commented Oct 8, 2016

Can we have a test to make sure this fails? I'd write it myself but not sure how.

struct Foo<A> { inner: A };

trait Bar { fn bar(); }

impl Bar for Foo<i32> {
    fn bar() {
        Self { inner: 1.5f32 };
    }
}

@petrochenkov
Copy link
Contributor Author

@Ericson2314
Added the test.

@Ericson2314
Copy link
Contributor

Thanks!

@nrc
Copy link
Member

nrc commented Oct 8, 2016

This feels a little weird to me since Self is a fully instantiated type, but in patterns and struct initialisers, you can usually only supply a type constructor. So the semantics are kind of special when using Self in an impl where parameters are instantiated - I presume you are just using the name from Self, effectively? I guess we have the same problem when type aliases are partial instantiations? This feels worse on a theoretical level because we are admitting a different syntactic kind, but I suspect there may not be a practical difference.

@petrochenkov
Copy link
Contributor Author

petrochenkov commented Oct 8, 2016

@nrc

in patterns and struct initialisers, you can usually only supply a type constructor.

I've heard something about this being true before 1.0, but now paths in struct expressions/patterns are types, possibly with provided type arguments, not just type constructors.

struct S<T, U = u16> {
    a: T,
    b: U,
}

type Alias<T> = S<T>;

fn main() {
    // Everything below is legal
    let s = S::<u8, u8> { a: 0, b: 1 }; // desugared into S::<u8, u8> { a: 0, b: 1 };
    let s = S::<u8> { a: 0, b: 1 }; // desugared into S::<u8, u16> { a: 0, b: 1 };
    let s = S { a: 0, b: 1 }; // desugared into S::<_, _> { a: 0, b: 1 };
    let s = Alias::<u16> { a: 0, b: 1 }; // desugared into S::<u16, u16> { a: 0, b: 1 };
}

So, Self being already instantiated is not an exception.

@nrc
Copy link
Member

nrc commented Oct 9, 2016

@petrochenkov huh, ok, then I feel much better about this, thanks for the explanation!

Does this mean that if the type params or lifetime params of the values for fields disagree with the params of Self, then there will be an error? Could you add a test for that please?

@petrochenkov
Copy link
Contributor Author

@nrc

Could you add a test for that please?

@Ericson2314 asked for the same test a few messages above and I added compile-fail/struct-path-self-type-mismatch.rs

@Ericson2314
Copy link
Contributor

@nrc not knowing that feature prior to this also led me to think of the test case. :)

@nrc
Copy link
Member

nrc commented Oct 11, 2016

@petrochenkov sweet, thanks!

@aturon
Copy link
Member

aturon commented Oct 11, 2016

What about the common case of constructors with generics?

struct Foo<T> { ... }

impl<T> Foo<T> {
    fn new<U>(u: U) -> Foo<U> {
        Self { ... } // is this Foo<T> or Foo<U>?
    }
}

@aturon
Copy link
Member

aturon commented Oct 11, 2016

I'm going to nominate for merging on @eddyb's behalf:

@rfcbot fcp merge

@durka
Copy link
Contributor

durka commented Oct 11, 2016

@aturon I think you might need a T- label.

About fn new<U> -- how common is it really that Foo<T>::new doesn't return Foo<T>‽ Anyway, I would expect that code to fail with a type mismatch. Indeed it does (twice):

[root@li1424-173 rust]# build/x86_64-unknown-linux-gnu/stage1/bin/rustc - <<<"struct Foo<T> { inner: T } impl<T> Foo<T> { fn new<U>(u: U) -> Foo<U> { Self { inner: u } } } fn main() {}"                │··
error[E0308]: mismatched types                                                                                                                                                                           │··
 --> <anon>:1:87                                                                                                                                                                                         │··
  |                                                                                                                                                                                                      │··
1 | struct Foo<T> { inner: T } impl<T> Foo<T> { fn new<U>(u: U) -> Foo<U> { Self { inner: u } } } fn main() {}                                                                                           │··
  |                                                                                       ^ expected type parameter, found a different type parameter                                                    │··
  |                                                                                                                                                                                                      │··
  = note: expected type `T`                                                                                                                                                                              │··
  = note:    found type `U`                                                                                                                                                                              │··
                                                                                                                                                                                                         │··
error[E0308]: mismatched types                                                                                                                                                                           │··
 --> <anon>:1:73                                                                                                                                                                                         │··
  |                                                                                                                                                                                                      │··
1 | struct Foo<T> { inner: T } impl<T> Foo<T> { fn new<U>(u: U) -> Foo<U> { Self { inner: u } } } fn main() {}                                                                                           │··
  |                                                                         ^^^^^^^^^^^^^^^^^ expected type parameter, found a different type parameter                                                  │··
  |                                                                                                                                                                                                      │··
  = note: expected type `Foo<U>`                                                                                                                                                                         │··
  = note:    found type `Foo<T>`                                                                                                                                                                         │··
                                                                                                                                                                                                         │··
error: aborting due to 2 previous errors

@petrochenkov can we get this to work for tuple structs as well?

[root@li1424-173 rust]# build/x86_64-unknown-linux-gnu/stage1/bin/rustc - <<<"struct Foo<T>(T); impl<T> Foo<T> { fn new<U>(u: U) -> Foo<U> { Self(u) } } fn main() {}"                                   │··
error[E0425]: unresolved name `Self`                                                                                                                                                                     │··
 --> <anon>:1:64                                                                                                                                                                                         │··
  |                                                                                                                                                                                                      │··
1 | struct Foo<T>(T); impl<T> Foo<T> { fn new<U>(u: U) -> Foo<U> { Self(u) } } fn main() {}                                                                                                              │··
  |                                                                ^^^^ unresolved name                                                                                                                  │··
                                                                                                                                                                                                         │··
error: aborting due to previous error

Note that Self { 0: u } does work.

@aturon aturon added the T-lang Relevant to the language team, which will review and decide on the PR/issue. label Oct 11, 2016
@aturon
Copy link
Member

aturon commented Oct 11, 2016

Let's try that again, with a team:

@rfcbot fcp merge

@eddyb
Copy link
Member

eddyb commented Oct 11, 2016

@durka Type aliases also don't work for tuple structs - but you can have a function with the same name.
Not sure if we should do something specific for Self in the value namespace, but it seems orthogonal.

@durka
Copy link
Contributor

durka commented Oct 11, 2016

Oh, I didn't know that type aliases don't work for tuple structs. And you don't even get a warning, just an error when you try to use the alias. That's super weird, but yeah orthogonal to this PR.

@rfcbot
Copy link

rfcbot commented Oct 11, 2016

FCP proposed with disposition to merge. Review requested from:

No concerns currently listed.
See this document for info about what commands tagged team members can give me.

@aturon
Copy link
Member

aturon commented Oct 11, 2016

@durka

how common is it really that Foo<T>::new doesn't return Foo<T>

I feel like I've run into cases like this from time to time, though probably in all cases it suffices to make a separate impl block. The example I gave is not a realistic one; I'll try to dig up some real code.

In any case, I think the proposed behavior for this PR is fine there.

@nrc
Copy link
Member

nrc commented Oct 11, 2016

@aturon

What about the common case of constructors with generics?

AIUI, this is a case of the case I was asking about and your example should give a type error since Self is considered Foo<T>.

@petrochenkov
Copy link
Contributor Author

Added this example #37035 (comment) to the tests.

@rfcbot
Copy link

rfcbot commented Oct 13, 2016

All relevant subteam members have reviewed. No concerns remain.

@rfcbot
Copy link

rfcbot commented Oct 20, 2016

It has been one week since all blocks to the FCP were resolved.

@nikomatsakis
Copy link
Contributor

OK, seems like we're all on board with letting this change go forward.

@bors
Copy link
Contributor

bors commented Oct 27, 2016

📌 Commit 077eed2 has been approved by eddyb

@bors
Copy link
Contributor

bors commented Oct 27, 2016

☔ The latest upstream changes (presumably #36695) made this pull request unmergeable. Please resolve the merge conflicts.

Diagnostics for struct path resolution errors in resolve and typeck are unified.
Self type is treated as a type alias in few places (not reachable yet).
Unsafe cell is seen in constants even through type aliases.
All checks for struct paths in typeck work on type level.
@petrochenkov
Copy link
Contributor Author

@bors r=eddyb

@bors
Copy link
Contributor

bors commented Oct 27, 2016

📌 Commit 8a38928 has been approved by eddyb

@bors
Copy link
Contributor

bors commented Oct 28, 2016

⌛ Testing commit 8a38928 with merge 5530030...

bors added a commit that referenced this pull request Oct 28, 2016
Support `Self` in struct expressions and patterns

Struct expressions and patterns generally support type aliases `Alias { field: 10 }` i.e. they already have to work with `ty::Ty` to do their job. `Self` is a type alias (when it's not a type parameter) => struct expressions and patterns should support `Self`.

Typical example:
```
impl MyStruct {
    fn new() -> Self {
        Self { a: 10, b: "Hello" }
    }
}
```

The first commit does some preparations and cleanups, see the commit message for  details.
This also fixes couple of bugs related to aliases in struct paths (fixes #36286).

EDIT:
Since struct expressions and patterns always work with `ty::Ty` now, associated paths in them are also supported. If associated type `A::B` successfully resolves to a struct (or union) type, then `A::B { /* fields */ }` is a valid expression/pattern. This will become more important when enum variants are treated as [associated items](#26264 (comment)).

r? @eddyb
@bors bors merged commit 8a38928 into rust-lang:master Oct 28, 2016
@bluss bluss added the relnotes Marks issues that should be documented in the release notes of the next release. label Oct 28, 2016
@nikomatsakis
Copy link
Contributor

Discussing in the @rust-lang/core team meeting, we realized that this probably ought to have been feature-gated!

@nikomatsakis
Copy link
Contributor

Even though it's small, it seems like feature-gating (and moving to stabilize in next cycle) would be more proper.

@aturon
Copy link
Member

aturon commented Nov 3, 2016

👍 for feature-gating.

sophiajt pushed a commit to sophiajt/rust that referenced this pull request Nov 5, 2016
Feature gate Self and associated types in struct expressions and patterns

cc rust-lang#37544
Fixes rust-lang#37035 (comment)
r? @nikomatsakis
@petrochenkov petrochenkov deleted the selfstruct branch March 16, 2017 19:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
relnotes Marks issues that should be documented in the release notes of the next release. T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Trait bounds are not respected when type aliases are used in struct expressions
10 participants