Skip to content

Recover from malformed trait bounds of the form Fn<'a>() #103490

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Closed
fmease opened this issue Oct 24, 2022 · 5 comments · Fixed by #104531
Closed

Recover from malformed trait bounds of the form Fn<'a>() #103490

fmease opened this issue Oct 24, 2022 · 5 comments · Fixed by #104531
Assignees
Labels
A-diagnostics Area: Messages for errors, warnings, and lints A-parser Area: The lexing & parsing of Rust source code to an AST A-suggestion-diagnostics Area: Suggestions generated by the compiler applied by `cargo fix` D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@fmease
Copy link
Member

fmease commented Oct 24, 2022

The following code:

fn f(_: impl FnOnce<'a>(&'a str) -> bool) {}

currently produces a heap of semi-helpful diagnostics:

stderr (7 errors)
error: unexpected lifetime `'a` in pattern
 --> src/lib.rs:1:26
  |
1 | fn f(_: impl FnOnce<'a>(&'a str) -> bool) {}
  |                          ^^ help: remove the lifetime

error: expected one of `:` or `|`, found `->`
 --> src/lib.rs:1:34
  |
1 | fn f(_: impl FnOnce<'a>(&'a str) -> bool) {}
  |                                  ^^ expected one of `:` or `|`

error: expected one of `)`, `+`, `,`, or `::`, found `(`
 --> src/lib.rs:1:24
  |
1 | fn f(_: impl FnOnce<'a>(&'a str) -> bool) {}
  |                        ^
  |                        |
  |                        expected one of `)`, `+`, `,`, or `::`
  |                        help: missing `,`

error[E0261]: use of undeclared lifetime name `'a`
 --> src/lib.rs:1:21
  |
1 | fn f(_: impl FnOnce<'a>(&'a str) -> bool) {}
  |                     ^^ undeclared lifetime
  |
  = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html
help: consider making the bound lifetime-generic with a new `'a` lifetime
  |
1 | fn f(_: impl for<'a> FnOnce<'a>(&'a str) -> bool) {}
  |              +++++++
help: consider introducing lifetime `'a` here
  |
1 | fn f<'a>(_: impl FnOnce<'a>(&'a str) -> bool) {}
  |     ++++

error[E0658]: the precise format of `Fn`-family traits' type parameters is subject to change
 --> src/lib.rs:1:14
  |
1 | fn f(_: impl FnOnce<'a>(&'a str) -> bool) {}
  |              ^^^^^^^^^^ help: use parenthetical notation instead: `FnOnce() -> ()`
  |
  = note: see issue #29625 <https://github.com/rust-lang/rust/issues/29625> for more information

error[E0107]: this trait takes 0 lifetime arguments but 1 lifetime argument was supplied
 --> src/lib.rs:1:14
  |
1 | fn f(_: impl FnOnce<'a>(&'a str) -> bool) {}
  |              ^^^^^^---- help: remove these generics
  |              |
  |              expected 0 lifetime arguments

error[E0107]: this trait takes 1 generic argument but 0 generic arguments were supplied
 --> src/lib.rs:1:14
  |
1 | fn f(_: impl FnOnce<'a>(&'a str) -> bool) {}
  |              ^^^^^^ expected 1 generic argument
  |
help: add missing generic argument
  |
1 | fn f(_: impl FnOnce<'a, Args>(&'a str) -> bool) {}
  |                       ++++++

when instead we could special-case this and emit a single (or at least fewer) diagnostic that suggests the following:

- impl FnOnce<'a>(&'a str) -> bool
+ impl for<'a> FnOnce(&'a str) -> bool

If the bound in not inside impl Trait, like in:

fn f<F>(_: F) where F: FnOnce<'a>(&'a str) -> bool {}

we currently emit the following:

error: expected one of `+`, `,`, `::`, or `{`, found `(`
 --> src/lib.rs:1:34
  |
1 | fn f<F>(_: F) where F: FnOnce<'a>(&'a str) -> bool {}
  |                                  ^ expected one of `+`, `,`, `::`, or `{`

which is less verbose for sure but still, we should provide the same suggestion I propose above.

Related issue: #103487.

@rustbot label A-parser A-suggestion-diagnostics D-newcomer-roadblock

@fmease fmease added A-diagnostics Area: Messages for errors, warnings, and lints T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Oct 24, 2022
@rustbot

This comment was marked as resolved.

@rustbot rustbot added A-parser Area: The lexing & parsing of Rust source code to an AST A-suggestion-diagnostics Area: Suggestions generated by the compiler applied by `cargo fix` D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. labels Oct 24, 2022
@ohno418
Copy link
Contributor

ohno418 commented Oct 25, 2022

I'd like to take a look.
@rustbot claim

@ohno418
Copy link
Contributor

ohno418 commented Nov 2, 2022

@fmease
I'm wondering if taking an approach that suggests using higher-rank trait bounds (for<'a> Fn) for an Fn-family trait with a lifetime parameter. Is it appropriate?
I'm concerned that it may not a comprehensive solution because HRTB can be used outside of the Fn-family traits (as I understand).
Or is it okay only for the Fn-family traits for now?

@fmease
Copy link
Member Author

fmease commented Nov 2, 2022

I am not sure I fully understand what you mean.

Fn-family trait with a lifetime parameter

As you probably know, none of Fn, FnMut, FnOnce take a lifetime parameter and probably never will. Of course, you could define such a trait with unstable features but there is no way to use them, so it's not relevant:

#![feature(unboxed_closures)]

#[rustc_paren_sugar]
pub trait Tr<'a, T> { type Output; }

pub fn f(_: impl Tr(i32)) {} //~ ERROR this trait takes 1 lifetime argument but
                             //        0 lifetime arguments were supplied

Or is it okay only for the Fn-family traits for now?

Yes, I'd say so. I wouldn't look for names however (like FnOnce, std::ops::FnMut, …) but just look for an opening parenthesis ( after a generic parameter list. This way, we can emit the same suggestion for the following case (where the name is X):

fn f(_: impl X<'a>(&'a str)) {}
use FnOnce as X;

@ohno418
Copy link
Contributor

ohno418 commented Nov 3, 2022

just look for an opening parenthesis ( after a generic parameter list.

Ah, definitely. Thank you!

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
A-diagnostics Area: Messages for errors, warnings, and lints A-parser Area: The lexing & parsing of Rust source code to an AST A-suggestion-diagnostics Area: Suggestions generated by the compiler applied by `cargo fix` D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants