rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
98.3k stars 12.72k forks source link

Diagnostics do not consider lifetime parameters to be "generics" #128583

Open QuineDot opened 3 months ago

QuineDot commented 3 months ago

Code

fn example<'one: 'two, 'two, Three, const Four: usize, Five>() {}

fn main() {
    example::<()>();
}

Current output

error[E0107]: function takes 3 generic arguments but 1 generic argument was supplied
 --> src/main.rs:4:5
  |
4 |     example::<()>();
  |     ^^^^^^^   -- supplied 1 generic argument
  |     |
  |     expected 3 generic arguments
  |
note: function defined here, with 3 generic parameters: `Three`, `Four`, `Five`
 --> src/main.rs:1:4
  |
1 | fn example<'one: 'two, 'two, Three, const Four: usize, Five>() {}
  |    ^^^^^^^                   -----  -----------------  ----
help: add missing generic arguments
  |
4 |     example::<(), Four, Five>();
  |                 ++++++++++++

Desired output

error[E0107]: function takes 3 non-lifetime arguments but 1 non-lifetime argument was supplied
 --> src/main.rs:4:5
  |
4 |     example::<()>();
  |     ^^^^^^^   -- supplied 1 non-lifetime argument
  |     |
  |     expected 3 non-lifetime arguments
  |
note: function defined here, with 3 non-lifetime parameters: `Three`, `Four`, `Five`
 --> src/main.rs:1:4
  |
1 | fn example<'one: 'two, 'two, Three, const Four: usize, Five>() {}
  |    ^^^^^^^                   -----  -----------------  ----
help: add missing non-lifetime arguments
  |
4 |     example::<(), Four, Five>();
  |                 ++++++++++++

Rationale and extra context

Lifetimes parameters are also generic parameters, so the statement that the example has 3 generic arguments is counterfactual. For function items in particular, early-bound lifetimes are also generic parameters of the function item type.

See this URLO topic for an example of someone who understands the distinction between late and early lifetime parameters being confused by the current diagnostic (being mislead that a function's lifetime parameter was late bound because it "only had 2 generic parameters" based on the diagnostic).

Other cases

//! This version shows that the compiler currently outputs two
//! separate warnings, one for lifetime parameters and one for
//! non-lifetime parameters
fn example<'one: 'two, 'two, Three, const Four: usize, Five>() {}

fn main() {
    example::<'static, ()>();
}
/*

error[E0107]: function takes 2 lifetime arguments but 1 lifetime argument was supplied
 --> src/main.rs:4:5
  |
4 |     example::<'static, ()>();
  |     ^^^^^^^   ------- supplied 1 lifetime argument
  |     |
  |     expected 2 lifetime arguments
  |
note: function defined here, with 2 lifetime parameters: `'one`, `'two`
 --> src/main.rs:1:4
  |
1 | fn example<'one: 'two, 'two, Three, const Four: usize, Five>() {}
  |    ^^^^^^^ ----        ----
help: add missing lifetime argument
  |
4 |     example::<'static, 'static, ()>();
  |                      +++++++++

error[E0107]: function takes 3 generic arguments but 1 generic argument was supplied
 --> src/main.rs:4:5
  |
4 |     example::<'static, ()>();
  |     ^^^^^^^            -- supplied 1 generic argument
  |     |
  |     expected 3 generic arguments
  |
note: function defined here, with 3 generic parameters: `Three`, `Four`, `Five`
 --> src/main.rs:1:4
  |
1 | fn example<'one: 'two, 'two, Three, const Four: usize, Five>() {}
  |    ^^^^^^^                   -----  -----------------  ----
help: add missing generic arguments
  |
4 |     example::<'static, (), Four, Five>();
  |                          ++++++++++++
*/
*/

Rust Version

Rust playground

Stable channel
Build using the Stable version: 1.80.0

Beta channel
Build using the Beta version: 1.81.0-beta.2
(2024-07-25 08328a323ecd80b443a8)

Nightly channel
Build using the Nightly version: 1.82.0-nightly
(2024-08-02 fd8d6fbe505ecf913f5e)

Anything else?

No response

QuineDot commented 3 months ago

The error used to say "type arguments" instead of "generic arguments" which was accurate at the time. It presumably changed in 1.54 in anticipation of const generics and/or the ability to intermix generic const and types, which I believe both stabilized in 1.59. (Prior to that const parameters had to come after type parameters.)

This is the PR. Looks like before it was "lifetime", "type", "const" and after it was "lifetime", "generic".