-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Tracking issue for const fn pointers #63997
Comments
I think that, at the very least, this should work: const fn foo() {}
const FOO: const fn() = foo;
const fn bar() { FOO() }
const fn baz(x: const fn()) { x() }
const fn bazz() { baz(FOO) } For this to work:
Currently, const fn foo() {}
let x: const fn() = foo;
let y: fn() = x; // OK: const fn => fn coercion I don't see any problems with supporting this. The RFC mentions some issues, but I don't see anything against just supporting this restricted subset. This subset would be super useful. For example, you could do: struct Foo<T>(T);
trait Bar { const F: const fn(Self) -> Self; }
impl<T: Bar> Foo<T> {
const fn new(x: T) -> Self { Foo(<T as Bar>::F(x)) }
}
const fn map_i32(x: i32) -> i32 { x * 2 }
impl Bar for i32 { const F: const fn(Self) -> Self = map_i32; }
const fn map_u32(x: i32) -> i32 { x * 3 }
impl Bar for u32 { const F: const fn(Self) -> Self = map_u32; } which is a quite awesome work around for the lack of trait Bar { const fn map(self) -> Self; }
impl Bar for i32 { ... }
impl Bar for u32 { ... }
// or const impl Bar for i32 { ... { This is also a way to avoid having to use |
AFAIK |
Don't know, @Centril suggested that I open one. I have no idea why EDIT: If I call a non- |
Lotsa things aren't RFCed with respect to |
it is. you can do const fn f() {}
let x = f;
x(); inside a constant. But this information is lost when casting to a function pointer. Function pointers just don't have the concept of of constness. |
Figuring out constness in function pointers or dyn traits is a tricky questions, with a lot of prior discussion in the RFC and the pre-RFC. |
whats about? |
I was fiddling with something while reading some of the discussion around adding a pub struct Lazy<T, F = fn() -> T> { ... } Here's a simple (but not very useful) example that hits this. Adding another type parameter for the function makes it work on stable but it feels unnecessary. Could this specific case be allowed without stabilizing the entire ball of wax here? (Specifically: referencing and storing |
The reason we can't do this is that this would mean we'd lock ourselves into the syntax that |
Just in case other people run into this being unstable: It's still possible to use function pointers in #[repr(transparent)]
struct Wrap<T>(T);
extern "C" fn my_fn() {}
const FN: Wrap<extern "C" fn()> = Wrap(my_fn);
struct Struct {
fnptr: Wrap<extern "C" fn()>,
}
const fn still_const() -> Struct {
Struct {
fnptr: FN,
}
} |
If const is a qualifier of function like const fn foo() { }
fn bar() { }
fn main() {
let x = if true { foo } else { bar };
} It compiles now but not compiles with these changes because |
Const fn's are still fns, the type won't be charged. In fact const qualifier only allows const fn appear in const contexes, and be evaluated at compile time. You can think of this like implicit casting. |
unsafe fn foo() { }
fn bar() { }
fn main() {
let x = if true { foo } else { bar };
} This code does not compile for the same reason. Unsafe fn's are still fns too, why we haven't implicit casting in this situation? |
This leads to possible unsafety in our code, and all which comes with it. fn bar() {}
const fn foo(){}
const fn foo_bar(){
if true { foo() } else { bar() };
} This must not compile, because bar is not const and therefore can't be evaluated at compile time. Btw, it raises(?) "can't call non const fn inside of const one", and can be considered incorrect downcasting. (set of valid const fns is smaller than set of fns at all) |
I think you're missing the point. With implicit coercions, the type of
Of course this must not compile, but I don't think that's related to what @filtsin was talking about. |
Personally I would love to see more implicit coercions for function pointers. Not sure how feasible that is though. I've previously wanted trait implementations for function pointer types to be considered when passing a function (which has a unique type) to a higher-order generic function. I posted about it on internals, but it didn't receive much attention. |
But not in oposite direction - thats what i wanted to say. |
impl<T> Cell<T> {
pub fn with<U>(&self, func: const fn(&mut T) -> U) -> U;
} Wouldn't const fn pointers make this sound? A const fn can't access a static or a thread-local, which would make this unsound. Edit: a cell containing a reference to itself makes this unsound |
It seems assignment is currently broken, complaining about casts, even though no such casts are actually performed. (A const function simply assigning a function ptr basically). #83033 |
See my response at #83033 (comment) -- short summary: there is in fact a cast going on here; see the reference on "function item types" for more details. |
I've found a case where this isn't true. (playground link) struct Handlers([Option<fn()>; _]);
impl Handlers {
const fn new() -> Self {
Self([None; _])
}
} |
Yea, just like with dyn Trait or generic trait bounds, there are workarounds to our checks and I'm convinced now we should just allow all of these like we do in const items. You can't call them, but you can pass them around. |
…r=wesleywiser Stabilize const_fn_fn_ptr_basics, const_fn_trait_bound, and const_impl_trait # Stabilization Report This PR serves as a request for stabilization for three const evaluation features: 1. `const_fn_fn_ptr_basics` 2. `const_fn_trait_bound` 3. `const_impl_trait` These are being stabilized together because they are relatively minor and related updates to existing functionality. ## `const_fn_fn_ptr_basics` Allows creating, passing, and casting function pointers in a `const fn`. The following is an example of what is now allowed: ```rust const fn get_function() -> fn() { fn foo() { println!("Hello, World!"); } foo } ``` Casts between function pointer types are allowed, as well as transmuting from integers: ```rust const fn get_function() -> fn() { unsafe { std::mem::transmute(0x1234usize) } } ``` However, casting from a function pointer to an integer is not allowed: ```rust const fn fn_to_usize(f: fn()) -> usize { f as usize //~ pointers cannot be cast to integers during const eval } ``` Calling function pointers is also not allowed. ```rust const fn call_fn_ptr(f: fn()) { f() //~ function pointers are not allowed in const fn } ``` ### Test Coverage The following tests include code that exercises this feature: - `src/test/ui/consts/issue-37550.rs` - `src/test/ui/consts/issue-46553.rs` - `src/test/ui/consts/issue-56164.rs` - `src/test/ui/consts/min_const_fn/allow_const_fn_ptr_run_pass.rs` - `src/test/ui/consts/min_const_fn/cast_fn.rs` - `src/test/ui/consts/min_const_fn/cmp_fn_pointers.rs` ## `const_fn_trait_bound` Allows trait bounds in `const fn`. Additionally, this feature allows creating and passing `dyn Trait` objects. Examples such as the following are allowed by this feature: ```rust const fn do_thing<T: Foo>(_x: &T) { // ... } ``` Previously only `Sized` was allowed as a trait bound. There is no way to call methods from the trait because trait methods cannot currently be marked as const. Allowing trait bounds in const functions does allow the const function to use the trait's associated types and constants. This feature also allowes `dyn Trait` types. These work equivalently to non-const code. Similar to other pointers in const code, the value of a `dyn Trait` pointer cannot be observed. Note that due to rust-lang#90912, it was already possible to do the example above as follows: ```rust const fn do_thing<T>(_x: &T) where (T,): Foo { // ... } ``` ### Test Coverage The following tests include code that exercises `const_fn_trait_bound`: - `src/test/ui/consts/const-fn.rs` - `src/test/ui/consts/issue-88071.rs` - `src/test/ui/consts/min_const_fn/min_const_fn.rs` - `src/test/ui/consts/min_const_fn/min_const_fn_dyn.rs` - `src/test/ui/nll/issue-55825-const-fn.rs` - Many of the tests in `src/test/ui/rfc-2632-const-trait-impl/` also exercise this feature. ## `const_impl_trait` Allows argument and return position `impl Trait` in a `const fn`, such as in the following example: ```rust const fn do_thing(x: impl Foo) -> impl Foo { x } ``` Similar to generic parameters and function pointers, this allows the creation of such opaque types, but not doing anything with them beyond accessing associated types and constants. ### Test Coverage The following tests exercise this feature: - `src/test/ui/type-alias-impl-trait/issue-53096.rs` - `src/test/ui/type-alias-impl-trait/issue-53678-generator-and-const-fn.rs` ## Documentation These features are documented along with the other const evaluation features in the Rust Reference at https://doc.rust-lang.org/stable/reference/const_eval.html. There is a PR that updates this documentation to reflect the capabilities enabled by these features at rust-lang/reference#1166. Tracking issues: rust-lang#57563, rust-lang#63997, rust-lang#93706
For const closures a lot has happened in 2022. This now works since #![feature(const_trait_impl)]
const fn foo<T: ~const Fn() -> i32>(f: &T) -> i32 {
f()
} And since #![feature(const_trait_impl)]
const fn foo(f: &impl ~const Fn() -> i32) -> i32 {
f()
} For const fn pointers, nothing much has happened though. This still errors: const fn foo(f: ~const fn() -> i32) -> i32 {
f()
} gives
Edit: note that while what I said is great progress, the two aren't well comparable, as the #![feature(const_trait_impl)]
const fn foo(f: &dyn ~const Fn() -> i32) -> i32 {
f()
} |
Seems to have been patched. You need
|
Sub-tracking issue for #57563.
This tracks
const fn
types and callingfn
types inconst fn
.From the RFC (https://github.com/oli-obk/rfcs/blob/const_generic_const_fn_bounds/text/0000-const-generic-const-fn-bounds.md#const-function-pointers):
const
function pointersis illegal before and with this RFC. While we can change the language to allow this feature, two
questions make themselves known:
fn pointers in constants
is already legal in Rust today, even though the
F
doesn't need to be aconst
function.Opt out bounds might seem unintuitive?
Alternatively one can prefix function pointers to
const
functions withconst
:This opens up the curious situation of
const
function pointers in non-const functions:Which is useless except for ensuring some sense of "purity" of the function pointer ensuring that
subsequent calls will only modify global state if passed in via arguments.
The text was updated successfully, but these errors were encountered: