Skip to content

Stabilize anonymous_lifetime_in_impl_trait #107378

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

Closed
wants to merge 1 commit into from

Conversation

c410-f3r
Copy link
Contributor

@c410-f3r c410-f3r commented Jan 27, 2023

Stabilization proposal

This PR proposes the stabilization of #![feature(anonymous_lifetime_in_impl_trait)].

Version: 1.69 (beta => 2023-03-09, stable => 2023-04-20).

What is stabilized

For non-asynchronous functions, allows the usage of anonymous lifetimes in APITs.

fn _example(_: impl Iterator<Item = &()>) {}

Motivation

In addition to ergonomics, the lack of parity between asynchronous and non-asynchronous functions can be confusing as the former is already allowed on stable toolchains.

// OK!
async fn _example(_: impl Iterator<Item = &()>) {}

History

cc @cjgillot

@rustbot
Copy link
Collaborator

rustbot commented Jan 27, 2023

r? @estebank

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

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jan 27, 2023
@apiraino
Copy link
Contributor

apiraino commented Feb 9, 2023

I think this was reviewed and can be switched to waiting on author. Feel free to request a review with @rustbot ready, thanks!

edit: I retract the review switch (I had missed the comments in the review)

@rustbot ready

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Feb 9, 2023
@c410-f3r
Copy link
Contributor Author

c410-f3r commented Feb 9, 2023

Well, I am actually waiting for a FCP that should be started by the lang or compile team but didn't receive any feedback yet 🤷

@cjgillot cjgillot added T-lang Relevant to the language team, which will review and decide on the PR/issue. I-lang-nominated Nominated for discussion during a lang team meeting. and removed T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Feb 9, 2023
@cjgillot
Copy link
Contributor

cjgillot commented Feb 9, 2023

Submitting to lang team consideration for stabilization.

@joshtriplett joshtriplett added the relnotes Marks issues that should be documented in the release notes of the next release. label Feb 14, 2023
@joshtriplett
Copy link
Member

We discussed this in today's @rust-lang/lang meeting, and we think this is ready for an FCP to merge:

@rfcbot merge

We'd also like to make sure that future work on type-alias impl Trait (TAIT) doesn't automatically assume anonymous lifetimes will work there, and thinks carefully about how or if that should work.

@rfcbot
Copy link
Collaborator

rfcbot commented Feb 14, 2023

Team member @joshtriplett has proposed to merge this. The next step is review by the rest of the tagged team members:

Concerns:

Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

cc @rust-lang/lang-advisors: FCP proposed for lang, please feel free to register concerns.
See this document for info about what commands tagged team members can give me.

@rfcbot rfcbot added proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. labels Feb 14, 2023
@joshtriplett joshtriplett removed the I-lang-nominated Nominated for discussion during a lang team meeting. label Feb 14, 2023
@c410-f3r
Copy link
Contributor Author

c410-f3r commented Feb 15, 2023

We discussed this in today's @rust-lang/lang meeting, and we think this is ready for an FCP to merge:

@rfcbot merge

We'd also like to make sure that future work on type-alias impl Trait (TAIT) doesn't automatically assume anonymous lifetimes will work there, and thinks carefully about how or if that should work.

Just to confirm, the following snippet that is currently allowed will be denied due to possible future compatibility issues.

#![feature(anonymous_lifetime_in_impl_trait, type_alias_impl_trait)]

type Foo<'a> = impl IntoIterator<Item = &'a i32>;

// TAIT won't work without this function
pub fn type_resolution<'a>(slice: &'a [i32]) -> Foo<'a> {
    slice
}

pub fn bar<'a>(slice: &'a [i32]) {
    foo([type_resolution(slice)]);
}

// Implies foo<'a>(_: impl IntoIterator<Item = Foo<'a>) {}
pub fn foo(_: impl IntoIterator<Item = Foo>) {}

If that is really the case, then I will create a PR to address such concern.

@bors
Copy link
Collaborator

bors commented Feb 17, 2023

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

Copy link
Contributor

@estebank estebank left a comment

Choose a reason for hiding this comment

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

r=me on the changes after t-lang approval and rebase

@QuineDot
Copy link

Lifetime elision seems to ignore the anonymous lifetime parameter introduced.

Example 1.

async fn does_not_compile(mut iter: impl Iterator<Item = &()>) -> &() {
    iter.next().unwrap()
}

async fn compiles<'a>(mut iter: impl Iterator<Item = &'a ()>) -> &'a () {
    iter.next().unwrap()
}

async fn wrong_error(iter: &mut impl Iterator<Item = &()>) -> &() {
    iter.next().unwrap()
}
Error output
error[[E0106]](https://doc.rust-lang.org/stable/error_codes/E0106.html): missing lifetime specifier
 --> src/lib.rs:1:67
  |
1 | async fn does_not_compile(mut iter: impl Iterator<Item = &()>) -> &() {
  |                                                                   ^ expected named lifetime parameter
  |
  = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
help: consider using the `'static` lifetime
  |
1 | async fn does_not_compile(mut iter: impl Iterator<Item = &()>) -> &'static () {
  |                                                                    +++++++

error: lifetime may not live long enough
 --> src/lib.rs:2:5
  |
1 | async fn does_not_compile(mut iter: impl Iterator<Item = &()>) -> &() {
  |                                                                   --- return type `impl Future<Output = &'static ()>` contains a lifetime `'1`
2 |     iter.next().unwrap()
  |     ^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static`

error: lifetime may not live long enough
  --> src/lib.rs:10:5
   |
9  | async fn wrong_error(iter: &mut impl Iterator<Item = &()>) -> &() {
   |                            -                                  --- return type `impl Future<Output = &()>` contains a lifetime `'1`
   |                            |
   |                            let's call the lifetime of this reference `'2`
10 |     iter.next().unwrap()
   |     ^^^^^^^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`

For more information about this error, try `rustc --explain E0106`.
error: could not compile `playground` due to 3 previous errors

And similarly for non-async (on nightly):

#![feature(anonymous_lifetime_in_impl_trait)]

trait Trait<'a> {
    fn foo(&self) -> &'a str { "" }
}

// Comment this to see the other two errors
pub fn f(t: impl Trait<'_>) -> &str {
    t.foo()
}

pub fn g(t: &impl Trait<'_>) -> &str {
    t.foo()
}

pub fn parse_reg(it: &mut impl Iterator<Item=&Token>) -> Result<&str, String> {
    let c = it.next().unwrap();
    match c {
        Token::Text(text) => Ok(text.as_str()),
        _ => Err(format!("Expected register got {:?}", c))
    }
}
Error output
error[[E0106]](https://doc.rust-lang.org/nightly/error_codes/E0106.html): missing lifetime specifier
 --> src/lib.rs:8:32
  |
8 | pub fn f(t: impl Trait<'_>) -> &str {
  |                                ^ expected named lifetime parameter
  |
  = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
help: consider using the `'static` lifetime
  |
8 | pub fn f(t: impl Trait<'_>) -> &'static str {
  |                                 +++++++

For more information about this error, try `rustc --explain E0106`.
error: lifetime may not live long enough
  --> src/lib.rs:15:5
   |
14 | pub fn g(t: &impl Trait<'_>) -> &str {
   |          -  - let's call the lifetime of this reference `'2`
   |          |
   |          has type `t`
15 |     t.foo()
   |     ^^^^^^^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`

error: lifetime may not live long enough
  --> src/lib.rs:27:30
   |
24 | pub fn parse_reg(it: &mut impl Iterator<Item=&Token>) -> Result<&str, String> {
   |                  --  - let's call the lifetime of this reference `'2`
   |                  |
   |                  has type `it`
...
27 |         Token::Text(text) => Ok(text.as_str()),
   |                              ^^^^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`

error: could not compile `playground` (lib) due to 2 previous errors

The feature should IMO treat the introduced anonymous lifetime like one introduced by a reference.

  • Is the only input lifetime, and thus is the output lifetime, for fn does_not_compile and fn f
  • Causes ambiguity errors due to more than one input lifetime for fn wrong_error, fn g, and fn parse_reg

@tmandry
Copy link
Member

tmandry commented Mar 22, 2023

This was discussed in the lang team meeting yesterday (notes). We felt that the PR description needed more detail before we can decide on stabilizing this.

As in the Zulip discussion, it seems there are multiple ways of interpreting this elision.

fn _example(_: impl Iterator<Item = &()>) {}
  1. Adding a lifetime parameter to the function: fn _example<'a>(_: impl Iterator<Item = &'a ()>) {}
  2. Higher-ranked bound: fn _example(_: for<'a> impl Iterator<Item = &'a ()>) {}

There needs to be an argument why (1) is the correct behavior to stabilize, if we go with that.

@rfcbot concern why-not-higher-rank

We'd also like the PR to lay out how anonymous lifetimes will behave in the following cases (and other interesting cases if we didn't think of them):

  • impl Fn(&u32)
  • impl PartialEq<&u32>
  • impl Iterator<Item = &u32>
  • impl Foo<'_> (does '_ behave the same as elision in every case)

as well as whether/how we might extend this behavior to where clauses.

@rfcbot concern elaborate-cases-and-future-directions

@c410-f3r
Copy link
Contributor Author

Well, it is a surprising statement because my understanding is that all rules governing anonymous lifetimes in non-async functions should equal their async counterparts that is currently stable. Any difference between both will probably induce unnecessary confusion for users.

Anyway, I will try to elaborate a report taking into consideration the above assumption as well as the team's concerns in the following days/weeks.

@tmandry
Copy link
Member

tmandry commented Oct 30, 2023

I would be happy to start with the subset that @nikomatsakis proposed. @c410-f3r are you interested in making those changes?

@c410-f3r
Copy link
Contributor Author

Personally, I need more time to digest all the information, but at first glance it seems that the current behaviour matches the desired subset.

However, if it is necessary to perform more than a feature removal and/or test management, then I probably won't have the time to empirically hack the compiler to achieve the goals in the near future.

@rust-log-analyzer

This comment has been minimized.

@tmandry
Copy link
Member

tmandry commented Oct 31, 2023

This proposal is to only allow elided lifetimes in equality bounds of non-generic associated types. I think the changes from the current behavior would be:

  1. Elided lifetimes are not allowed in the generics of the trait itself.
  2. Elided lifetimes are not allowed in equality bounds of GATs. (Though I'm not sure if they are currently)

@bors
Copy link
Collaborator

bors commented Dec 11, 2023

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

@Dylan-DPC Dylan-DPC added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-team Status: Awaiting decision from the relevant subteam (see the T-<team> label). labels Feb 6, 2024
@Dylan-DPC
Copy link
Member

@c410-f3r any updates on this? thanks

@c410-f3r
Copy link
Contributor Author

I will try to get back to this PR next week

@c410-f3r
Copy link
Contributor Author

c410-f3r commented Mar 31, 2024

In regards to not stabilizing elided lifetimes in trait generics, the following is already allowed today.

trait FooBar<'a> {
    type Item;
}

async fn foo(_: impl FooBar<'_, Item = &u32>) {}

So leaving this case out for non-async functions does not help with the lack of parity.

If the elaboration of elided rules is not trivial and needs more investigation, then the accidental stabilization of elided lifetimes for asynchronous functions should probably be re-considered and possibly partially-reverted.

@c410-f3r c410-f3r force-pushed the stabilize-anon-lt branch 2 times, most recently from 70abdc8 to 5717003 Compare April 21, 2024 21:48
@traviscross
Copy link
Contributor

@rustbot labels +I-lang-nominated

We unnominated this back in October 2023 as more analysis seemed to be needed. Since then, @nikomatsakis and @tmandry have posted substantive analysis that it seems we should discuss.

@rustbot rustbot added the I-lang-nominated Nominated for discussion during a lang team meeting. label May 6, 2024
@bors
Copy link
Collaborator

bors commented Sep 1, 2024

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

@alex-semenyuk
Copy link
Member

@c410-f3r
From wg-triage. Do you have any updates on this PR?

@c410-f3r
Copy link
Contributor Author

Six months ago the suggestions of #107378 (comment) were properly addressed with one exception as commented in #107378 (comment).

The following allowed signatures are tested in the stabilized module of the tests/ui/impl-trait/impl-trait-lifetimes.rs file.

fn foo(x: impl Iterator<Item = &u32>) // equivalent to `'a` in `fn foo<'a>`
fn foo(&self) -> impl Iterator<Item = &u32> // equivalent to `'a` in `fn foo<'a>(&'a self)`

GAT-related signatures are tested and hopefully correctly ignored in the tests/ui/impl-trait/partial-anonymous-lifetime-in-impl-trait.rs file.

So, if fn foo(x: impl FooBar<_, Item = &u32> is indeed not allowed then the lack of parity between its allowed asynchronous counter-part is the only remaining concern IFAICT.

This PR is waiting for feedback, which is similar to the situation of #122808 (comment)

@alex-semenyuk alex-semenyuk added S-waiting-on-team Status: Awaiting decision from the relevant subteam (see the T-<team> label). and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Oct 19, 2024
@c410-f3r c410-f3r closed this Feb 24, 2025
@rfcbot rfcbot removed proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. labels Feb 24, 2025
@apiraino apiraino removed the I-lang-nominated Nominated for discussion during a lang team meeting. label Apr 10, 2025
@c410-f3r c410-f3r deleted the stabilize-anon-lt branch April 10, 2025 23:32
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. S-waiting-on-team Status: Awaiting decision from the relevant subteam (see the T-<team> label). 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.