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

Do not suggest "using a local generic parameter" for constants #115720

Closed
MaxVerevkin opened this issue Sep 9, 2023 · 9 comments · Fixed by #115744
Closed

Do not suggest "using a local generic parameter" for constants #115720

MaxVerevkin opened this issue Sep 9, 2023 · 9 comments · Fixed by #115744
Assignees
Labels
A-diagnostics Area: Messages for errors, warnings, and lints A-suggestion-diagnostics Area: Suggestions generated by the compiler applied by `cargo fix` D-invalid-suggestion Diagnostics: A structured suggestion resulting in incorrect code. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@MaxVerevkin
Copy link

MaxVerevkin commented Sep 9, 2023

Code

fn main() {}

trait Trait {
    const X: u32;
}

impl Trait for () {
    const X: u32 = 0;
}

impl<T: Trait> Trait for [T] {
    const X: u32 = {
        const TEMP: u32 = T::X + 1;
        TEMP
    };
}

Current output

error[E0401]: can't use generic parameters from outer function
  --> src/main.rs:13:27
   |
11 | impl<T: Trait> Trait for [T] {
   |      - type parameter from outer function
12 |     const X: u32 = {
13 |         const TEMP: u32 = T::X + 1;
   |                   -       ^^^^ use of generic parameter from outer function
   |                   |
   |                   help: try using a local generic parameter instead: `<T>`

For more information about this error, try `rustc --explain E0401`.

Desired output

I'm not sure what the output should be (I'm not even sure why this does not compile), but using a local generic parameter does not make sense for me. It seems like the error message thinks this code is inside a function.

@MaxVerevkin MaxVerevkin 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 Sep 9, 2023
@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Sep 9, 2023
@chenyukang
Copy link
Member

chenyukang commented Sep 10, 2023

It's should be T::X instead of T::NUM, right?

This code can compile successfully:

fn main() { }

trait Trait {
    const X: u32;
}

impl Trait for () {
    const X: u32 = 0;
}

impl<T: Trait> Trait for [T] {
    const X: u32 = {
         let tmp: u32 = T::X + 1;
         tmp
    };
}

This should also be ok:

const X: u32 = {
         const TEMP: u32 = T::X + 1;
         TEMP
    };

emm, this error seems need to be fixed:

11 | impl<T: Trait> Trait for [T] {
   |      - type parameter from outer function
12 |     const X: u32 = {
13 |          const TEMP: u32 = T::X + 1;
   |                    -       ^^^^ use of generic parameter from outer function
   |                    |
   |                    help: try using a local generic parameter instead: `<T>`

@MaxVerevkin
Copy link
Author

It's should be T::X instead of T::NUM, right?

Yes, I updated the code.

@fmease fmease removed the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Sep 10, 2023
@fmease fmease self-assigned this Sep 10, 2023
@fmease
Copy link
Member

fmease commented Sep 10, 2023

It should be worth mentioning that this suggestion only occurs on beta and nightly but not on stable.
I'm certain that this regressed when I introduced generic const items (#113522).

@fmease
Copy link
Member

fmease commented Sep 10, 2023

This should also be ok: [const TEMP instead of let temp]

No, inner items (like constants, functions, static items, impl items) are not allowed to reference the generics of their parent item since inner items are just like top-level items, they are compiled separately.

The exception being associated items which may reference the generics of their parent.

@MaxVerevkin
Copy link
Author

This should also be ok: [const TEMP instead of let temp]

No, inner items (like constants, functions, static items, impl items) are not allowed to reference the generics of their parent item since inner items are just like top-level items, they are compiled separately.

Is this a current limitation or a design decision? Is there any discussion on this topic?

For context I was trying to implement a trait that (statically) describes Rust types with DBus type signatures. The idea fell apart on arrays:

impl<T: Type> Type for [T] {
    const SIGNATURE: &'static str = const_format::concatcp!("a", T::SIGNATURE);
}

which fails with a very similar error message as in my example.

@fmease
Copy link
Member

fmease commented Sep 10, 2023

That's unfortunate. At first glance, I don't see a good way to fix const_format. People have already reported this limitation: rodrimati1992/const_format_crates#48. I played around a bit with generic_const_items and generic_const_exprs enabled to find a solution but got stuck on errors like overly complex generic constant // field access is not supported in generic constants.

I haven't been around for long enough but I'd wager it was an intentional decision for ease of use (no accidental size explosions due to quadratic / polynomial monomorphization?) and implementation.
Changing it would be breaking (in corner cases). There were some recent discussions about it on Zulip (warning: a lot of text):

@fmease fmease added A-suggestion-diagnostics Area: Suggestions generated by the compiler applied by `cargo fix` D-invalid-suggestion Diagnostics: A structured suggestion resulting in incorrect code. labels Sep 10, 2023
@MaxVerevkin
Copy link
Author

MaxVerevkin commented Sep 10, 2023

Hm, okay. I didn't even know about "generic const items". Using them sort of fixes the error in this case:

impl<T: Trait> Trait for [T] {
    const X: u32 = {
        const TEMP<T: Trait>: u32 = T::X + 1;
        TEMP::<T>
    };
}

I too don't see a way to use them to concatenate strings...

Still, the error message can be improved at least because it refers to some outer function, even if there are no functions involved.

@fmease
Copy link
Member

fmease commented Sep 10, 2023

I too don't see a way to use them to concatenate strings...

Yeah, I meant that I used cargo expand to expand const_format::concatp!("a", T::SIGNATURE) and tried to rewrite the expanded code to use generic_const_items and generic_const_exprs which didn't work out.

For sure, the diagnostic should definitely be improved, I'm gonna fix that.

@MaxVerevkin
Copy link
Author

I too don't see a way to use them to concatenate strings...

But I found a way to prepend bytes in my specific case. I'm not really sure if this is the intended way to use generic const items.

#![feature(generic_const_items, generic_const_exprs)]
#![allow(incomplete_features)]

fn main() {
    dbg!(<[i32]>::SIGNATURE);
    dbg!(<[u32]>::SIGNATURE);
    dbg!(<[&[u32]]>::SIGNATURE);
}

trait Type {
    const SIGNATURE: &'static str;
}

impl Type for i32 {
    const SIGNATURE: &'static str = "i";
}

impl Type for u32 {
    const SIGNATURE: &'static str = "u";
}

impl<T: Type + ?Sized> Type for &T {
    const SIGNATURE: &'static str = T::SIGNATURE;
}

impl<T: Type> Type for [T]
where
    [(); T::SIGNATURE.len() + 1]:,
{
    const SIGNATURE: &'static str = unsafe { std::str::from_utf8_unchecked(CONST_PREPEND_BYTE::<b'a', T>) };
}

const CONST_PREPEND_BYTE<const B: u8, T: Type>: &[u8; T::SIGNATURE.len() + 1] = {
    let mut arr = [0u8; T::SIGNATURE.len() + 1];
    let mut i = 0;
    arr[0] = B;
    while i < T::SIGNATURE.len() {
        arr[i + 1] = T::SIGNATURE.as_bytes()[i];
        i += 1;
    }
    & { arr } // This works but just `&arr` doesn't???
} where [(); T::SIGNATURE.len() + 1]:;

For sure, the diagnostic should definitely be improved, I'm gonna fix that.

Awesome :)

matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Sep 11, 2023
Improve diagnostic for generic params from outer items (E0401)

Generalize the wording of E0401 to talk about *outer items* instead of *outer functions* since the current phrasing is outdated. The outer item can be a function, constant, trait, ADT or impl block (see the new UI test for the more exotic examples).

Further, don't suggest introducing generic parameters to constant items unless the feature `generic_const_items` is enabled.

Lastly, make E0401 translatable while we're at it.

Fixes rust-lang#115720.
Dylan-DPC added a commit to Dylan-DPC/rust that referenced this issue Sep 11, 2023
Improve diagnostic for generic params from outer items (E0401)

Generalize the wording of E0401 to talk about *outer items* instead of *outer functions* since the current phrasing is outdated. The outer item can be a function, constant, trait, ADT or impl block (see the new UI test for the more exotic examples).

Further, don't suggest introducing generic parameters to constant items unless the feature `generic_const_items` is enabled.

Lastly, make E0401 translatable while we're at it.

Fixes rust-lang#115720.
@bors bors closed this as completed in 8b49731 Sep 11, 2023
rust-timer added a commit to rust-lang-ci/rust that referenced this issue Sep 11, 2023
Rollup merge of rust-lang#115744 - fmease:fix-e0401, r=compiler-errors

Improve diagnostic for generic params from outer items (E0401)

Generalize the wording of E0401 to talk about *outer items* instead of *outer functions* since the current phrasing is outdated. The outer item can be a function, constant, trait, ADT or impl block (see the new UI test for the more exotic examples).

Further, don't suggest introducing generic parameters to constant items unless the feature `generic_const_items` is enabled.

Lastly, make E0401 translatable while we're at it.

Fixes rust-lang#115720.
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-suggestion-diagnostics Area: Suggestions generated by the compiler applied by `cargo fix` D-invalid-suggestion Diagnostics: A structured suggestion resulting in incorrect code. 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.

4 participants