This issue tracks the progress of const fn
as well as const evaluation more broadly.
const fn
or const evaluation and only exists to provide links to other places that track the progress of specific issues. If you wish to discuss some subject related to const evaluation or const fn
, please find an existing appropriate issue below or create an new issue and comment here with a link to the newly created issue.If it's impossible to detect what issue the programmer should be pointed to, they can simply be pointed to this issue.
The const
related issues currently on deck are as follows:
#![feature(const_err)]
)usize
casts: #51910(#![feature(const_raw_ptr_to_usize_cast)]
)&mut T
references and borrows: #57349 (#![feature(const_mut_refs)]
)transmute
: #53605const fn(...)
/ fn(...)
): #63997const unsafe? extern fn
: #64926const fn
s: #69431mem::discriminant
: #69821(#![feature(const_discriminant)]
)Open RFCs:
?const
trait bound opt-out: #67794(#![feature(const_trait_bound_opt_out)]
)#![feature(const_trait_impl)]
)Planned RFCs:
const fn
(this includes everything related to Box
)Completed:
unsafe
operations: #55607The rustc_const_unstable
attribute should also contain an issue number, so each const feature can redirect to a specific tracking issue, rather than defaulting to https://github.com/rust-lang/rust/issues/24111, which is now closed (which might be misleading to users looking for an active issue).
As a rough starting point, you can search for occurrences of "rustc_const_unstable"
in the compiler to figure out how the attributes are being handled. This will require changing the diagnostics for rustc_const_unstable
to point to specific tracking issues. I can provide more detailed mentoring instructions if someone wants to tackle this.
Would it be appropriate to turn those bullet-points into checkboxes?
Also, should there be something in the list about using traits in const fn
contexts? It looks like #2237 was closed without making much headway.
I changed the word "still" to "currently" when giving the list.
Given your example there of a thing that needs to be considered but doesn't have an issue, I don't think that we should use a checkbox system. That implies a finality if we do check off all the boxes, but the work will probably be ongoing even then. Probably simpler to add and remove from the list as things are opened and closed.
...I don't know, I think that if the list had value 11 days ago, it will continue to have value as const fn
issues are opened or closed.
I think that some sort of list that tracks what's already done with const should reside elsewhere, but we can have check boxes I guess.
@BatmanAoD I assume you meant #53972?
Also, should there be something in the list about using traits in const fn contexts?
That is still being discussed in a pre-RFC: https://github.com/rust-rfcs/const-eval/pull/8
Once an RFC has been opened and merged, there'll be a tracking issue on this repo that we can link to from this issue
@tormol Nope, I hadn't run across that! Thanks for pointing me to it.
What about a string->int conversions?
I think doing a env!("SOMETHING").parse().unwrap()
is a wanted use case to be evaluated at compile time
Sounds totally reasonable to eventually have.
Make an issue and it can go on the list.
Are there tracking issues covering the issues in this example? cc @oli-obk
fn foo() -> &'static [u8] {
// ok
&[0, 1, 2, 3]
}
const fn bar() -> &'static [u8] {
// error[E0723]: unsizing casts are not allowed in const fn (see issue #57563)
// warning[E0515]: cannot return reference to temporary value
&[0, 1, 2, 3]
}
Resolving the error
seems like a straightforward extension. I don't think unsizing casts would draw much controversy or require much implementation effort.
The warning
requires a touch more explanation. Specifically, the non-const
version works because of rvalue static promotion and I believe the const
code should do the same. This means the array is never a temporary on the stack in the first place.
How about allowing traits like Add
and Sub
to be const as well?
I remember hearing about some talk for trait implementations to be able to be declared as a const implementation in the Discord, but I don't have the tracking number for that issue, and I don't even know if that's in an issue in the first place.
So, it's on someone's mind at least, and if you find it please comment here and I'll add it into the list.
Usage of traits in const context is a currently active RFC: https://github.com/rust-lang/rfcs/pull/2632
Is there a specific tracking issue for const TypeId?
For some reason, making a const fn that returns Vec::new()
doesn't result in the same assembly as creating the Vec::new()
as a const item:
#![feature(const_vec_new)]
const A: Vec<usize> = Vec::new();
pub const fn return_empty_vec() -> Vec<usize> { Vec::new() }
pub const fn return_vec_via_const_item() -> Vec<usize> { A }
Theoretically, both return_empty_vec
and return_vec_via_const_item
should result in the same assembly, but the assembly of return_empty_vec
is a lot worse: https://rust.godbolt.org/z/pTl7Eo
Both functions should technically result in this assembly:
example::return_vec_via_const_item:
mov rax, rdi
mov rcx, qword ptr [rip + .L__unnamed_2]
mov qword ptr [rdi], rcx
mov rcx, qword ptr [rip + .L__unnamed_2+8]
mov qword ptr [rdi + 8], rcx
mov rcx, qword ptr [rip + .L__unnamed_2+16]
mov qword ptr [rdi + 16], rcx
ret
.L__unnamed_2:
.asciz "\b\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
... but only return_vec_via_const_item
does.
However, when using -O
, now suddenly return_empty_vec
is better:
example::return_empty_vec:
mov rax, rdi
mov qword ptr [rdi], 8
xorps xmm0, xmm0
movups xmmword ptr [rdi + 8], xmm0
ret
Ideally, it would be great if this would be also the case for debug builds.
@fschutt I'm not sure this is actionable. I'd expect that a debug build does not inline function calls, so I'm assuming that return_empty_vec
will in fact contain a function call to Vec::new()
. Making a function const fn
does not mean calling it in a regular function will const evaluate the function. If you want to guarantee that const evaluation happens, use an explicit constant (as you did).
All const fn
guarantees is that you can call it at compile-time, not that it is automatically processed at compile-time at every use site.
@fschutt I don't believe debug builds are under any obligation to perform any optimization.
Using -O
makes the assembly for return_empty_vec
extremely simple: https://rust.godbolt.org/z/M8CcA3
Yes, obviously there's no "obligation" for rustc to do it, but "it would be nice" because rustcs runtime performance in debug builds is already sub-par. I usually build with -C opt-level=1
because at -C opt-level=0
my code runs at about 3fps (which is not a problem of my code, it's just that the performance of the debug-mode assembly is so extremely slow). At -C opt-level=1
I'd expect Rust to inline const fn and very small functions, but it doesn't do that: https://rust.godbolt.org/z/tWb6bU
So now I basically have to write something like:
#[cfg(debug_assertions)] { A } // to get better debug-runtime perf
#[cfg(not(debug_assertions))] { Vec::new() } // to get the best release mode assembly
Inlining and pre-computing the contents of a const fn
even in debug builds would be nice to have in order to improve the runtime performance in debug builds. There is not really a reason for a constant, pure function not to be precomputed. There is a 20x to 100x discrepancy between debug and release builds, which isn't a good thing because you either have to take the insanely long compile times of release mode or the 3fps code in debug mode (or --opt-level=1 code). While I'm no expert on this topic, it shouldn't take that much compile time to evaluate a const fn. Maybe I should do more benchmarks to prove my point.
This is entirely orthogonal to const fn imo. You're talking about optimizations that can happen to any kind of expression (e.g. 1+2
being 3
). Please open a separate issue, so we can talk about which optimizations should happen in debug mode.
@fschutt please ping me in said issue if you make it because I strongly agree with you. (oli is correct though, it doesn't go here)
The issue of what optimizations should occur in debug mode actually seems fairly open-ended to me (and probably not really even Rust-specific), so it may be more appropriate as a discussion topic on http://internals.rust-lang.org/ rather than here on GitHub.
I do agree that debug mode performance is important, and I appreciate your specific example of an unusable framerate. I'm not sure inlining is necessarily the answer, though. (I would guess that LLVM emits enough debug info to support stack traces through inline functions, but I don't actually know so.)
What's the status on const_str_as_bytes
? It seems like this should be easily stabilized, as it's currently just a simple field access (source code).
You have looked at the String::as_bytes
method, that feature gate is about str::as_bytes
, which has a less trivial body: https://github.com/rust-lang/rust/blob/00859e3e653973120006aaf3227823062dde1ba7/src/libcore/str/mod.rs#L2137-L2144
We have to figure out our transmute
and union
field access story before we can stabilize that method
Is there a more specific tracking issue for function pointers in const fns? e.g. https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=5455f0bdf725924403901eba36cdd58a
There's the const bounds RFC which has a section for function pointers in the future work part: https://github.com/oli-obk/rfcs/blob/const_generic_const_fn_bounds/text/0000-const-generic-const-fn-bounds.md#const-function-pointers
Are there any plans to support const fn
usage of OsStr::new
? It would be nice to be able to create OsStr
constants from string literals for things like filenames (see also Path::new
)
Yes, but that requires RFC 2632 https://github.com/rust-lang/rfcs/pull/2632 in order to be able to have stable const fn with trait bounds on their generic parameters
What about supporting trait bounds like the following?
trait Foo {
const INIT: Self;
}
struct Bar<T: Foo>(T);
impl<T: Foo> Bar<T> {
const fn new() -> Self {
Bar(T::INIT)
}
}
They shouldn't require a RFC AIUI.
A real-life example of those is in parking_lot::lock_api
, and it would be great if that could be done with stable.
Yeah, it's unfortunate that we can't use associated consts and marker trait bounds before the rest of it.
marker trait bounds
Or any other trait bound that is not relevant to the const fn, for that matter. e.g.
trait Foo {}
struct Bar<T: Foo>(PhantomData<T>);
impl<T: Foo> Bar<T> {
const fn new() -> Self {
Bar(PhantomData)
}
}
(although, a marker is involved, but it's not in the trait bound itself)
Perhaps a way to define this is: if a const fn
has a trait bound (perhaps inherited from an impl
block) but does not involve any of the items of that trait (methods, associated types, …), then it should be allowed. (The bound can still be relied on to satisfy other bounds, e.g. those on a struct
definition when constructing a value of that struct type.)
plus traits with associated consts (with no involvement from any other items of that trait)
While we could just allow const fns that don't use generics or don't actually use them to call functions, there are many questions about forward compatibility both in the language and in user defined const fn
. This topic is discussed on the RFC in detail.
The problem is not having trait bounds (so knowing that T: Foo
), it is declaring them (so the writing out of the T: Foo
) and what the semantics of declaring them are.
AFAICT, the RFC doesn't talk about associated consts and traits with no items involved. And I don't see what forward compatibility issues those could lead to.
TLDR:
const fn foo<T: Trait>() -> i32 { T::SomeAssocConst }
is fine
const fn foo<T: Trait>() -> i32 { T::some_fn() }
is not and the trait bounds are exactly the same. Also you don't want the body contents to have an effect on what types you are allowed to use as T
(some types may have some_fn
be const, so be ok, some may not have that). The RFC solves this question in a consistent manner.
Long version:
The RFC doesn't have to talk about associated consts, because as you correctly inferred, associated consts are completely unproblematic. We could totally add trait bounds and allow you to use associated consts. The problem is that adding trait bounds would also give you access to associated functions, and then the question comes up whether you are allowed to call them. The RFC answers this question. Adding trait bounds to const fn
s needs to answer all questions about the items on the traits at once, we can't separate these out. The RFC also explains why we need to clear up this question and not invent a new scheme for making functions callable later. If the RFC is unclear (or even just the motivation or summary section), please leave comments on the RFC so I can make that clearer.
More information on how we got here can be found in https://github.com/rust-lang/rust/issues/53972 and https://github.com/rust-rfcs/const-eval/issues/1
The problem is that adding trait bounds would also give you access to associated functions
Does it have to? There are number of things that currently can't be used in const functions, despite being part of the language, like match (IIRC, it's still not allowed). I can totally get that the compiler implementation to allow trait bounds only for non-problematic cases might be non-trivial (and I have no clue, it might as well be easy), but I'd argue that's an implementation detail.
I haven't used it in a while, but I recall that the const_fn feature on nightly already lets you use trait bounds in const fn. There's no methods callable because none are const, but it already allows "just marker traits" (and maybe associated consts?) perfectly fine.
Does it have to?
No, but once we allow trait bounds on the generic parameters of a function, and we allow you to call that without having a const impl
for the trait, we lock in into that system and can't ever go back. This is all explained in the RFC and linked discussions. If it weren't a forward compat issue you'd have trait bounds already. The RFC was created because we've had this exact discussion a year ago.
I haven't used it in a while, but I recall that the const_fn feature on nightly already lets you use trait bounds in const fn. There's no methods callable because none are const, but it already allows "just marker traits" (and maybe associated consts?) perfectly fine.
Yes, that's right. The RFC reexposes this feature via const fn foo<T: ?const Trait>()
that doesn't require a const impl
for the Trait
for the types passed to the function.
I would not create an RFC if it were unnecessary. I want these features as much as everyone else. The last const fn
issue ended up having a few hundred comments going back and forth on this topic. Please take the discussion to the RFC, this issue is not meant for it. Or create a new issue that we can link from here so we stop polluting the meta issue.
Or create a new issue that we can link from here so we stop polluting the meta issue.
👍 -- This issue is not for length discussion about a specific issue. If you want to discuss a topic in-depth, please create a new issue and link it here in a comment or use one of the existing linked issues at the top.
Seems like the last item "Const constructors: #61456" has been already implemented in https://github.com/rust-lang/rust/pull/61209 (as per Centril's comment https://github.com/rust-lang/rust/issues/61456#issuecomment-502383873)
Is there an issue for const fn
types ?
I expected this to work:
const fn foo() {}
const FOO: const fn() = foo;
const fn bar() { FOO() }
but it fails due to multiple issues:
const fn()
errors with const
is a keywordIs there a tracking issue for const
transmute? It's missing from the list and I couldn't find it in the issue tracker either?
Is there an issue for const fn types ?
I think that's part of the discussion around const fn
with (full) generics: https://github.com/rust-lang/rfcs/pull/2632.
@RalfJung https://github.com/rust-lang/rust/issues/53605 (adding to the list).
@gnzlbg Please file an issue if you believe one is lacking.
Would it be possible to special-case bool && bool
, bool || bool
, etc.? They can currently be performed in a const fn
, but doing so requires bitwise operators, which is sometimes unwanted.
@jhpratt Please move this to the relevant tracking issue (https://github.com/rust-lang/rust/issues/49146).
What about const async
,unsafe pointer arihmetics, const like a type modifier(returning const function from another one)?
https://github.com/rust-lang/rust/issues/64926 shoule be added to this tracking issue
What's the status on const_str_len
? I would be happy to contribute to its stabilization. However, I don't know where to find the next steps for that. Asking here since the unstable book links to this issue.
Nevermind, it's been stabilized for 1.39 at https://github.com/rust-lang/rust/pull/63770.
OsStr::new
would be cool.
Over last months functionality of constant functions has greatly improved. What is a current state of const functions in traits and use of generic parameters?
OsStr::new
would be cool.
That would require making AsRef
const fn, which first requires having const functions in traits (see the RFC https://github.com/rust-lang/rfcs/blob/master/text/0911-const-fn.md)
@Lokathor Please add https://github.com/rust-lang/rust/issues/66806 :)
What's the reason for not allowing trait bounds to be in scope of const fn
at all? I understand not allowing method calls based on the traits, but not allowing any trait bounds other than Sized
requires very ugly workarounds at times.
I almost feel like this is rejected by accident given the error message: "trait bounds other than Sized
on const fn parameters are unstable". Is the check too eager here, since this also triggers on const fn
that doesn't have any parameters? Or is it talking about type parameters specifically, even if the const fn
doesn't make use of them?
Trait bounds can still be used/enforced in const
/static
context, leading to workarounds such as this:
struct Outer<T: PartialEq> {
inner: Inner<T>,
}
struct Inner<T> {
_t: Option<T>,
}
impl<T: PartialEq> Outer<T> {
fn new() -> Self {
Self {
inner: Inner { _t: None, }
}
}
}
impl<T> Inner<T> {
const fn new() -> Self {
Self {
_t: None,
}
}
}
struct NoCmp;
static S: Outer<NoCmp> = Outer { inner: Inner::new() };
Yes this is an unfortunate limitation right now. The reason is that if we permitted this now, we may run into backwards compatibility issues when we try to permit calling methods of generic parameters in const fn later. There's an RFC that will permit both const fn with generic parameters on which to call methods and const fn
with generic parameters that may not be used in the const fn
: https://github.com/rust-lang/rfcs/pull/2632
Is the check too eager here, since this also triggers on const fn that doesn't have any parameters? Or is it talking about type parameters specifically, even if the const fn doesn't make use of them?
Any trait bounds on type parameters, even those on the type are available inside the const fn
and would thus cause the backwards compat issues.
Are Vec::new() and co. (so the data structure constructors that don't need any allocation) being tracked anywhere here? If not, I'd like to see those being const fn. Especially with parking_lot, spin and co. having const fn Mutex::new(...), it would be great to be able to create the data structures right away at compile time as well.
Vec::new is already a const fn: https://doc.rust-lang.org/std/vec/struct.Vec.html#method.new
being tracked anywhere
Many of these are already stable, so you'll need to be more specific.
const V: Vec<u8> = Vec::new();
const S: String = String::new();
This issue is not for discussion about specific extensions to const fn or const evaluation and only exists to provide links to other places that track the progress of specific issues. If you wish to discuss some subject related to const evaluation or const fn, please find an existing appropriate issue below or create an new issue and comment here with a link to the newly created issue.
Hi,please add #69345.
Also, shouldn't tracking issues only exist for things that are already unstably implemented, and are just waiting for stabilization?
Tracking issues also exist for accepted RFCs, which doesn't exist yet for const heap related things. So yes, I think we should close all issues around const heap by pointing to https://github.com/rust-lang/const-eval/issues/20
Is Box::new
tracked? Is it going to be ever possible at all?
Box::new
and generally any kind of heap allocation in const-eval is in pre-pre-RFC stage at https://github.com/rust-lang/const-eval/issues/20.
Please add #71499
Which issue blocks const ptr::copy_nonoverlapping
?
That would be at least https://github.com/rust-lang/rust/issues/57349 and https://github.com/rust-lang/rust/issues/51911.
@Lokathor Can we add const_caller_location to the list in the original comment?
Are there any plans/issues for slicing syntax? (i.e. &slice[..5]
)
Currently compiler refers to this meta issue, but I do not seem any related issue in list
@DoumanAsh that would fall under trait implementations.
I've been trying to find a tracking issue for defining const fn
s within traits.
say something like so
trait SomeTrait {
const fn some_method();
}
Or has this not yet been suggested within an rfc?
@Progdrasil check out impl const Trait
Most helpful comment
What about a string->int conversions?
I think doing a
env!("SOMETHING").parse().unwrap()
is a wanted use case to be evaluated at compile time