Open Lokathor opened 5 years ago
The 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.
EDIT: This has been resolved: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=5968ed38cb23d180f5842d9a0ba9b3e0
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)?
This issue tracks the progress of
const fn
as well as const evaluation more broadly.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 orconst 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: #85368mem::discriminant
: #69821(#![feature(const_discriminant)]
)const_maybe_uninit_assume_init
#86722Open 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 toBox
)Completed:
unsafe
operations: #55607