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

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

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 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 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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-diagnostics Area: Messages for errors, warnings, and lints A-parser Area: The 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