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

Const generics in closure arguments result in false "closure/generator type that references itself" error #85665

Open
mastopgunaf opened this issue May 25, 2021 · 6 comments
Labels
A-closures Area: closures (`|args| { .. }`) A-const-generics Area: const generics (parameters and arguments) C-bug Category: This is a bug. F-generic_const_exprs `#![feature(generic_const_exprs)]` requires-incomplete-features requires-nightly This issue requires a nightly compiler in some way.

Comments

@mastopgunaf
Copy link

mastopgunaf commented May 25, 2021

I tried compiling this code:

#![feature(const_generics)]
#![feature(const_evaluatable_checked)]

use std::convert::TryInto;

pub const PREFIX_LENGTH: usize = 4;

pub struct Foo<T, const L: usize> {
    template: &'static [u8; L],
    item: T
}

impl<T, const L: usize> Foo<T, L> {
    pub fn new<F>(template: &'static [u8; L], constructor: F) -> Self
        where F: Fn(&[u8; L - PREFIX_LENGTH]) -> T
    {
        Self {
            template: template,

            item: constructor(
                &TryInto::<[u8; L - PREFIX_LENGTH]>::try_into(
                    &template[PREFIX_LENGTH..]
                ).unwrap()
            )
        }
    }

    pub fn get(&self) -> &T {
        &self.item
    }
}

const TEMPLATE: &[u8; 12] = b"ABCD12345678";

fn main() {
    let foo = Foo::new(TEMPLATE,
        |x| x.to_vec()
    );

    println!("{}",
        String::from_utf8(
            foo.get().clone()
        ).unwrap()
    );
}

The output is supposed to be 12345678, but instead the compiler throws an error:

error[E0644]: closure/generator type that references itself
  --> src/main.rs:37:9
   |
37 |         |x| x.to_vec()
   |         ^^^^^^^^^^^^^^ cyclic type of infinite size
   |
   = note: closures cannot capture themselves or take themselves as argument;
           this error may be the result of a recent compiler bug-fix,
           see issue #46062 <https://github.com/rust-lang/rust/issues/46062>
           for more information

However, the code above doesn't include closures that capture themselves or take themselves as an argument.

The bug is most likely related to const generics, because omitting - PREFIX_LENGTH such that the const_generics feature is not used fixes the compilation. new function in this case is as follows:

    pub fn new<F>(template: &'static [u8; L], constructor: F) -> Self
        where F: Fn(&[u8; L]) -> T
    {
        Self {
            template: template,

            item: constructor(
                &TryInto::<[u8; L]>::try_into(
                    &template[..]
                ).unwrap()
            )
        }
    }

rustc --version --verbose:

rustc 1.54.0-nightly (126561cb3 2021-05-24)
binary: rustc
commit-hash: 126561cb31e8ebe1e2dd9dfd0d3ca621308dc56f
commit-date: 2021-05-24
host: x86_64-unknown-linux-gnu
release: 1.54.0-nightly
LLVM version: 12.0.1
@mastopgunaf mastopgunaf added the C-bug Category: This is a bug. label May 25, 2021
@mastopgunaf
Copy link
Author

mastopgunaf commented May 25, 2021

This bug can currently be circumvented by providing a function instead of a closure. This works:

fn item_constructor(data: &[u8; 8]) -> Vec<u8> {
	data.to_vec()
}

let foo = Foo::new(TEMPLATE, item_constructor);

But this does not:

let foo = Foo::new(TEMPLATE, |x| x.to_vec());

@Aaron1011
Copy link
Member

Minimized:

#![feature(const_generics)]
#![feature(const_evaluatable_checked)]

fn foo<F: FnOnce([u8; N - 1]), const N: usize>(val: F) {}

fn main() {
    foo(|arg: [u8; 5]| ());
}

@BoxyUwU
Copy link
Member

BoxyUwU commented Jun 6, 2021

I think this is likely caused by anon consts having all their parent's generics.
We end up with something like F: FnOnce([u8; ConstKind::Unevaluated({ N - 1 }, substs: [F, N])])

@jonas-schievink jonas-schievink added A-closures Area: closures (`|args| { .. }`) A-const-generics Area: const generics (parameters and arguments) labels Jun 6, 2021
@lcnr lcnr added the F-generic_const_exprs `#![feature(generic_const_exprs)]` label Dec 1, 2021
@ia0
Copy link
Contributor

ia0 commented Mar 23, 2022

Is there a workaround that doesn't need to monomorphize?

Also linking to the 2 relevant issues:

@ia0
Copy link
Contributor

ia0 commented Mar 24, 2022

Is there a workaround that doesn't need to monomorphize?

Actually found one: defunctionalization.

// before
... foo(|a| bar(a, x, y)) ...
... foo(|a| baz(a, z)) ...
fn foo(b: impl FnOnce(A)) {
    ... b(a) ...
}

// after
enum B {
    Bar(x: X, y: Y),
    Baz(z: Z),
}
... foo(B::Bar(x, y)) ...
... foo(B::Baz(z)) ...
fn foo(b: B) {
    ...
    match b {
        B::Bar(x, y) => bar(a, x, y),
        B::Baz(z) => baz(a, z),
    }
    ...
}

This way, A (which is [u8; N - 1] in the examples of previous comments) doesn't appear in foo signature, but bar and baz can still be polymorphic.

(In my example the closures are just function calls, so I don't need to introduce new top-level functions, but the example can be adapted to that case too.)

@lcnr
Copy link
Contributor

lcnr commented Jun 24, 2022

@workingjubilee workingjubilee added requires-nightly This issue requires a nightly compiler in some way. requires-incomplete-features labels Mar 4, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-closures Area: closures (`|args| { .. }`) A-const-generics Area: const generics (parameters and arguments) C-bug Category: This is a bug. F-generic_const_exprs `#![feature(generic_const_exprs)]` requires-incomplete-features requires-nightly This issue requires a nightly compiler in some way.
Projects
None yet
Development

No branches or pull requests

7 participants