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

Can't assign results of generic functions to const variables #57775

Open
moxian opened this issue Jan 20, 2019 · 10 comments
Open

Can't assign results of generic functions to const variables #57775

moxian opened this issue Jan 20, 2019 · 10 comments
Labels
A-diagnostics Area: Messages for errors, warnings, and lints A-traits Area: Trait system A-typesystem Area: The type system C-bug Category: This is a bug. needs-rfc This change is large or controversial enough that it should have an RFC accepted before doing it. T-lang Relevant to the language team, which will review and decide on the PR/issue.

Comments

@moxian
Copy link
Contributor

moxian commented Jan 20, 2019

fn foo<T:Default>() {
    const bar : T = T::default();
}

gives the following error (twice for some reason):

$ cargo +nightly build
   Compiling arrys_sizeoof v0.1.0 (C:\work\trash\repros\arrys_sizeoof)
error[E0401]: can't use type parameters from outer function
 --> src\lib.rs:2:17
  |
1 | fn foo<T:Default>() {
  |    --- - type variable from outer function
  |    |
  |    try adding a local type parameter in this method instead
2 |     const bar : T = T::default();
  |                 ^ use of type variable from outer function

error[E0401]: can't use type parameters from outer function
 --> src\lib.rs:2:21
  |
1 | fn foo<T:Default>() {
  |    --- - type variable from outer function
  |    |
  |    try adding a local type parameter in this method instead
2 |     const bar : T = T::default();
  |                     ^^^^^^^^^^ use of type variable from outer function

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0401`.
error: Could not compile `arrys_sizeoof`.

To learn more, run the command again with --verbose.

Changing let to const to let makes it compile just fine.

Another, slightly different example:

fn get_size<T:Sized>() {
    const size : usize = std::mem::size_of::<T>();
}

Here, the type of the variable is non-generic now, and it's only a function that is. Results in the same error, but only once now:

$ cargo +nightly build
   Compiling arrys_sizeoof v0.1.0 (C:\work\trash\repros\arrys_sizeoof)
error[E0401]: can't use type parameters from outer function
 --> src\lib.rs:2:46
  |
1 | fn get_size<T:Sized>() {
  |    -------- - type variable from outer function
  |    |
  |    try adding a local type parameter in this method instead
2 |     const size : usize = std::mem::size_of::<T>();
  |                                              ^ use of type variable from outer function

error: aborting due to previous error

For more information about this error, try `rustc --explain E0401`.
error: Could not compile `arrys_sizeoof`.

To learn more, run the command again with --verbose.

P.s.: if this is the desired behavior, it is confusing, since 1) changing const to let should not result in type error, 2) rustc --explain E0401 doesn't mention anything close - all the examples are about definition of new functions/types inside the function body (and it's unclear how to adapt that knowledge to this particular use case).

P.p.s.: the behavior is exactly the same on current stable (1.32.0)

@jonas-schievink
Copy link
Contributor

Well, what's intended here is that you're not allowed to use type parameters of an enclosing item in an inner item. const, static, trait, fn, impl are items, while let is a statement, so it's allowed in the latter case. I don't see an actual typing error though. And it makes sense that the error only shows once in the second example, because there you only use T once and not twice.

@Centril Centril added A-typesystem Area: The type system A-traits Area: Trait system A-diagnostics Area: Messages for errors, warnings, and lints labels Jan 20, 2019
@Centril
Copy link
Contributor

Centril commented Jan 20, 2019

Yep; @jonas-schievink is correct. Rust does not support what is referred to in Haskell as ScopedTypeVariables. Moreover, even if you could use the type parameter T, calling T::default() would fail since it's not a const expression.

cc @estebank re. possible improvements to diagnostics.

cc #57563.

@Centril Centril added C-feature-request Category: A feature request, i.e: not implemented / a PR. needs-rfc This change is large or controversial enough that it should have an RFC accepted before doing it. T-lang Relevant to the language team, which will review and decide on the PR/issue. labels Jan 20, 2019
@estebank
Copy link
Contributor

We need to stop suggesting adding a type argument when the requirement comes from a const, as they can't be generic, and replace it with a note stating that.

@estebank
Copy link
Contributor

From #48427, the same behavior is seen for statics

fn x<T: Default>() {
    static a: T = T::default();
}

#48427 (comment):

I think the big problem would be crate A instantiating static a by invoking crate B's x function. If then crate C also instantiates static a by invoking crate B's x function, you end up with two different a. So if crate D depends on A and C, you'll get very weird behaviour. Might be solvable with MIR only rlibs, but it's generally really easy to get this wrong.

Note that you can get around this limitation by creating another trait bound on T:

trait Foo: 'static {
    fn singleton() -> &'static Self {
        unimplemented!()
    }
}

fn x<T: Default + Foo>() {
    let a = T::singleton();
}

Side-note: you won't be able to call T::default() in a static constructor anyway

@Centril
Copy link
Contributor

Centril commented May 23, 2019

From #48427, the same behavior is seen for statics

@estebank It's the same for all nested items really; STV is the solution to all of them except for maybe impl items.

@zzau13
Copy link
Contributor

zzau13 commented Apr 7, 2022

Is there any news about this issue? Is it included in any roadmap? Can I do something to speed up its implementation?
Especially for this case:

fn x<T: Default>() {
    static a: T = T::default();
}

Thanks.

@estebank
Copy link
Contributor

estebank commented Apr 8, 2022

There has been no movement in supporting this at the lang level, and the diagnostic could do with some love to be clearer.

@yescallop
Copy link
Contributor

I also ran into this issue when I tried to do some compile-time assertions based on generic parameters from outer function.
Here's one workaround I thought of, which I hope will help:

fn foo<T>() {
    // can't use generic parameters from outer function:
    // const ASSERT: () = assert!(mem::size_of::<T>() == 8);
    let _ = Assert::<T>::SIZE_EQ_8;
}

struct Assert<T> {
    _marker: PhantomData<T>,
}

impl<T> Assert<T> {
    const SIZE_EQ_8: () = assert!(mem::size_of::<T>() == 8);
}

@Ternvein
Copy link

Ternvein commented Nov 8, 2022

I can confirm that sometimes it leads to very strange compilation errors. This code compiles fine:

use core::marker::PhantomData;

struct A<T>(PhantomData<T>);
impl<T> A<T> {
    const EMPTY: Vec<T> = Vec::new();
    
    fn f() -> [Vec<T>; 2] {
        [Self::EMPTY; 2]
    }
}

fn main() {
    let empty = A::<u32>::f();
    dbg!(empty);
}

while this does not:

fn f<U>() -> [Vec<U>; 2] {
    const EMPTY: Vec<U> = Vec::new();
    [EMPTY; 2]
}

fn main() {
    let empty = f::<u32>();
    dbg!(empty);
}

@Dylan-DPC Dylan-DPC added C-bug Category: This is a bug. and removed C-feature-request Category: A feature request, i.e: not implemented / a PR. labels Dec 5, 2023
@fmease
Copy link
Member

fmease commented Feb 2, 2024

@Ternvein, that is expected. While free constant items may not reference the generics of the outer item, associated constant items are allowed to reference the generics of their direct parent (i.e., trait, trait impl or impl).

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-traits Area: Trait system A-typesystem Area: The type system C-bug Category: This is a bug. needs-rfc This change is large or controversial enough that it should have an RFC accepted before doing it. T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

9 participants