Skip to content

Can't write generic const fns #116354

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

Open
ilyvion opened this issue Oct 2, 2023 · 8 comments
Open

Can't write generic const fns #116354

ilyvion opened this issue Oct 2, 2023 · 8 comments
Assignees
Labels
A-const-eval Area: Constant evaluation, covers all const contexts (static, const fn, ...) C-enhancement Category: An issue proposing an enhancement or a PR with one. F-const_refs_to_cell `#[feature(const_refs_to_cell)]` T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team, which will review and decide on the PR/issue.

Comments

@ilyvion
Copy link

ilyvion commented Oct 2, 2023

I was going to leave this as a remark on #80384, but you're apparently supposed to make separate issues instead of leaving comments on tracking issues, so here goes:

I was just playing around and couldn't even get past this simple code before getting yelled at:

const fn foo<T, const N: usize>(values: [T; N]) -> [T; N] {
    let slice = values.as_slice();
    let len = slice.len();
    
    values
}

Both as_slice() and len() are declared as const fn, so you'd think they'd be fine to use in another const fn, but no:

error[E0658]: cannot borrow here, since the borrowed element may contain interior mutability
 --> src/lib.rs:2:17
  |
2 |     let slice = values.as_slice();
  |                 ^^^^^^^^^^^^^^^^^
  |
  = note: see issue #80384 <https://github.com/rust-lang/rust/issues/80384> for more information

I don't even understand what this is trying to tell me. Issue #80384, which is the one linked, does not at all explain the source/cause of this error. I'm not working with UnsafeCell or interior mutability here. I'm attempting to make a &[T] from a [T; N].

@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Oct 2, 2023
@asquared31415
Copy link
Contributor

Consider the following code:

let arr = [UnsafeCell::new(1_u32)];
foo(arr);

your code would then have T = UnsafeCell<u32>, then make a &[UnsafeCell<u32>] which is rejected because that could potentially be used as interior mutability, which is rejected in const.

@ilyvion
Copy link
Author

ilyvion commented Oct 3, 2023

If UnsafeCell can't be used in const, why are some of its methods const fns? This all seems like a rather bad series of choices.

@Cerber-Ursi
Copy link
Contributor

UnsafeCell can't be used in const, but can be (indirectly, with some Sync wrapper, like Mutex) used in static:

use core::cell::UnsafeCell;

struct RacyCell<T>(UnsafeCell<T>);
unsafe impl<T> Sync for RacyCell<T> {}

static CELL: RacyCell<u8> = RacyCell(UnsafeCell::new(42));

And static initializers can't call non-const functions.

@workingjubilee
Copy link
Member

workingjubilee commented Oct 4, 2023

If UnsafeCell can't be used in const, why are some of its methods const fns? This all seems like a rather bad series of choices.

We actually intend to make mutability in const fn possible, and that will probably include UnsafeCell<T> (eventually). This is (mostly) the const_mut_ref feature.

@ilyvion
Copy link
Author

ilyvion commented Oct 4, 2023

I don't suppose it'd be possible for the compiler to detect the presence of UnsafeCell in the specific monomorphization of T in const fn foo<T, const N: usize>(values: [T; N]) -> [T; N] rather than just blanket disallow usage of all generics/mutability in const?

For instance,

const fn foo<const N: usize>(values: [String; N]) -> [String; N] {
    let slice = values.as_slice();
    let len = slice.len();
    
    values
}

compiles just fine while

use std::cell::UnsafeCell;

struct Foo(UnsafeCell<()>);

const fn foo<const N: usize>(values: [Foo; N]) -> [Foo; N] {
    let slice = values.as_slice();
    let len = slice.len();
    
    values
}

does not -- clearly the compiler knows that String doesn't have interior mutability and that Foo does; couldn't this same "test" be done on a per-T basis rather than as on a blanket for-all-T basis?

@workingjubilee
Copy link
Member

workingjubilee commented Oct 4, 2023

What you are describing (sometimes called a "post-monomorphization error") is both absurdly controversial (Rust allows them to happen, but it's still controversial in terms of API design), and also will become unnecessary the moment const_mut_refs stabilizes, so, uh,

Probably not?

@ilyvion
Copy link
Author

ilyvion commented Oct 4, 2023

Not to put too fine a point on it, but "when feature X stabilizes" in Rust can mean "by the next version" or "in over a decade," so that doesn't mean much and could leave a huge class of useful generic const functions unwritable for a very long time for the sake of making something unnecessary later.

But if it's basically a policy (or at least the uncontroversial approach) that it's better to error out on the basis of "any given T can violate something" rather than on the basis of "a specific T can violate something," there's not much to do about it I suppose.

@workingjubilee
Copy link
Member

Oh, I agree. I'm basically always pushing for more const stuff reaching anywhere near stable precisely because of things like this. There's... currently a lot of "huge class(es) of useful generic const functions (that are) unwritable".

I think the hardest problem here is that even if you PRed the changes necessary to the compiler to make this happen, you'd mostly be trying to convince all the people who are trying to fix the exact problems to simply make this work take a look at your code which may make their work take longer as they now have to work around it as well.

Also it would prooobably be another feature with gating, and probably work to test and stabilize it even if accepted, not just a "hey let's do this" and then in it goes into 1.75? So!

@workingjubilee workingjubilee added C-enhancement Category: An issue proposing an enhancement or a PR with one. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. A-const-fn F-const_mut_refs `#![feature(const_mut_refs)]` T-lang Relevant to the language team, which will review and decide on the PR/issue. needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. and removed needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. labels Oct 5, 2023
@oli-obk oli-obk self-assigned this Oct 5, 2023
@workingjubilee workingjubilee removed the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Oct 6, 2023
@oli-obk oli-obk added F-const_refs_to_cell `#[feature(const_refs_to_cell)]` and removed F-const_mut_refs `#![feature(const_mut_refs)]` labels Mar 14, 2024
@RalfJung RalfJung added A-const-eval Area: Constant evaluation, covers all const contexts (static, const fn, ...) and removed A-const-fn labels Dec 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-const-eval Area: Constant evaluation, covers all const contexts (static, const fn, ...) C-enhancement Category: An issue proposing an enhancement or a PR with one. F-const_refs_to_cell `#[feature(const_refs_to_cell)]` T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. 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

7 participants