rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
98.54k stars 12.74k forks source link

Meta tracking issue for `const fn` #57563

Open Lokathor opened 5 years ago

Lokathor commented 5 years ago

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 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:


Open RFCs:

Planned RFCs:


Completed:

varkor commented 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.

BatmanAoD commented 5 years ago

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.

Lokathor commented 5 years ago

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.

BatmanAoD commented 5 years ago

...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.

Lokathor commented 5 years ago

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.

tormol commented 5 years ago

@BatmanAoD I assume you meant #53972?

oli-obk commented 5 years ago

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

BatmanAoD commented 5 years ago

@tormol Nope, I hadn't run across that! Thanks for pointing me to it.

elichai commented 5 years ago

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

Lokathor commented 5 years ago

Sounds totally reasonable to eventually have.

Make an issue and it can go on the list.

solson commented 5 years ago

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]
}

playground link

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

agalakhov commented 5 years ago

How about allowing traits like Add and Sub to be const as well?

Lokathor commented 5 years ago

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.

Nemo157 commented 5 years ago

Usage of traits in const context is a currently active RFC: https://github.com/rust-lang/rfcs/pull/2632

elichai commented 5 years ago

@Lokathor https://github.com/rust-lang/rust/issues/59133

passcod commented 5 years ago

Is there a specific tracking issue for const TypeId?

fschutt commented 5 years ago

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.

oli-obk commented 5 years ago

@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.

BatmanAoD commented 5 years ago

@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

fschutt commented 5 years ago

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.

oli-obk commented 5 years ago

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.

Lokathor commented 5 years ago

@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)

BatmanAoD commented 5 years ago

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.)

jhpratt commented 5 years ago

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).

oli-obk commented 5 years ago

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

sfackler commented 5 years ago

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

oli-obk commented 5 years ago

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

tarcieri commented 5 years ago

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)

oli-obk commented 5 years ago

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

glandium commented 5 years ago

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.

Lokathor commented 5 years ago

Yeah, it's unfortunate that we can't use associated consts and marker trait bounds before the rest of it.

glandium commented 5 years ago

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)

SimonSapin commented 5 years ago

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.)

glandium commented 5 years ago

plus traits with associated consts (with no involvement from any other items of that trait)

oli-obk commented 5 years ago

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.

glandium commented 5 years ago

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.

oli-obk commented 5 years ago

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 fns 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.

oli-obk commented 5 years ago

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

glandium commented 5 years ago

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.

Lokathor commented 5 years 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.

oli-obk commented 5 years ago

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.

Centril commented 5 years ago

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.

mexus commented 5 years ago

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)

gnzlbg commented 5 years ago

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:

RalfJung commented 5 years ago

Is 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.

Centril commented 5 years ago

@RalfJung https://github.com/rust-lang/rust/issues/53605 (adding to the list).

@gnzlbg Please file an issue if you believe one is lacking.

gnzlbg commented 5 years ago

Done: https://github.com/rust-lang/rust/issues/63997

jhpratt commented 5 years ago

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.

Centril commented 5 years ago

@jhpratt Please move this to the relevant tracking issue (https://github.com/rust-lang/rust/issues/49146).

tema3210 commented 5 years ago

What about const async,unsafe pointer arihmetics, const like a type modifier(returning const function from another one)?