rust-lang/rust

Argument-position `impl Trait` requires a named lifetime

cramertj opened this issue · 11 comments

This function produces an "expected lifetime parameter" error:

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

This code should instead be accepted and bound the impl Trait parameter by the elided lifetime.

cc #34511

In my opinion, we should accept '_, in-band lifetimes, and friends in where clauses as well. I'm not 100% sure whether this was controversial though, have to check.

Wait, was this fixed in #49251 or not?

Still generates an error for me on nightly.

@cramertj Hmm yes, I should have tried first, my bad. This is independent of the two return-position impl Trait lifetime-related bugs that you're fixing, right?

CAD97 commented

Ran into this today. Providing the '_ lifetime gives a very fun error:

fn f(_: impl Iterator<Item = &'_ ()>) {}
error[E0106]: missing lifetime specifier
 --> src/lib.rs:1:31
  |
1 | fn f(_: impl Iterator<Item = &'_ ()>) {}
  |                               ^^ expected lifetime parameter

It's actually gotten slightly worse:

error[E0106]: missing lifetime specifier
 --> src/lib.rs:1:31
  |
1 | fn f(_: impl Iterator<Item = &'_ ()>) {}
  |                               ^^ expected named lifetime parameter
  |
help: consider introducing a named lifetime parameter
  |
1 | fn f(_: 'a, impl Iterator<Item = &'a ()>) {}
  |         ^^                       ^^^

#68583 will address that. After it gets merged the output will be

Screen Shot 2020-01-30 at 11 55 07 AM

I feel that's going to be as good a diagnostic we're gonna have for this.

runiq commented

I feel that's going to be as good a diagnostic we're gonna have for this.

@estebank Does that mean that this will be the "final" state for this feature? As in, anonymous lifetimes will not be usable in the positions mentioned by @nikomatsakis in #49287 (comment)?

What I mean that until anonymous lifetimes are accepted in impl Trait we're going to have some diagnostic, and the one I posted is likely the best we can do given our constraints.

The reason that lifetimes were not permitted here, as I recall, was some uncertainty about what they should mean (in particular, should impl Foo<'_> mean impl for<'a> Foo<'a> or what). I am actually still a bit unsure as to my opinion here -- I've seen people expect it to mean both things.

In this case, impl for<'a> Foo<'a> wouldn't work, but could you show me a case where it would? I feel that the only case where impl for<'a> X is common is for Fn() and in that case unnamed lifetimes usually work, and in the case where a named lifetime that hasn't been introduced is used produces the following now (not yet in the latest nightly):

error[E0261]: use of undeclared lifetime name `'a`
 --> file4.rs:1:27
  |
1 | fn foo(_compare: impl Fn(&'a u8)) {
  |                           ^^ undeclared lifetime
  |
  = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html
help: consider introducing lifetime `'a` here
  |
1 | fn foo<'a>(_compare: impl Fn(&'a u8)) {
  |       ^^^^
help: consider making the bound lifetime-generic with a new `'a` lifetime
  |
1 | fn foo(_compare: impl for<'a> Fn(&'a u8)) {
  |                       ^^^^^^^

I'm not sure what you mean by "show you a case where it would work", I guess you mean a realistic example where that is what you would want? I don't really have one off the top of my head, but I agree with you it's probably quite a bit less common -- after all, I almost never write for bounds in practice.

So yeah, I pretty much agree it'd be the wrong meaning, and I think it'd even be somewhat counter-intuitive. We have a pretty strong precedent right now that '_ binds into the "innermost enclosing parentheses", in a sense, and I would be quite surprising to find impl Foo<'_> create a lifetime bound within the scope of impl keyword.