-
Notifications
You must be signed in to change notification settings - Fork 13k
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
const fn
taking function pointers and generic args with trait bounds
#53972
Comments
Regarding making const the default, I meant in function definitions. Is anyone seriously proposing that the syntax for various kinds of function pointer types should use different keywords than the syntax for function definitions? const fn foo2(f: const Foo) {
const fn bar1(f: const Bar) { Wait, |
I don't understand this statement. Can you elaborate? |
I was initially responding to
… which seemed to suggest that somehow a type |
(I'm fine with this being discussed here, but whatever else, changes proposed here will absolutely require one or several RFCs... and just to be super clear, I'm wary of doing anything quickly here...)
The idea with such a design (I'm not for such a design atm...) is that you wouldn't have
This has large implications for const generics; me and @varkor discussed such ideas on Discord wrt. const-sigma types. In particular, this implies to me that you would write With respect to: struct Bar(const fn());
const fn bar(f: Bar) {
(f.0)() // not allowed
} I find that deeply surprising; by construction we know that |
In that case you couldn't have const fn new(f: fn()) -> Bar {
Bar(f)
} without additionally having some sort of |
I thought we have rust-lang/const-eval#1 for this discussion...? |
const fn new(f: fn()) -> Bar {
Bar(f)
} You have defined |
We treat @RalfJung I'll summarize the findings of this issue in rust-lang/const-eval#1 at the end of the week and then close this issue. I totally forgot about that issue ^^ |
Ok. So there are various ifs here. Summarizing the state by using function pointers as a placeholder scapegoat. The same logic applied to function pointers can be applied to fn foo() { if random() { println!("foo") } }
const F: fn() = foo; is legal in stable Rust today. This means that const G: () = F(); cannot be evaluated, because from the signature of It seems consistent to also say that in a function const fn bar(f: fn()) { ... } we cannot call Thus, I argue, we need new syntax to specify that a function pointer is callable at compile-time. Let's for a moment assume that we can use const fn bar(f: const fn()) { f() }
const fn bar2(f: fn()) -> fn() { f }
const fn bar3(f: const fn()) -> fn() { f(); f }
const fn bar4(f: const fn()) -> const fn() { f(); f } would all be legal Rust. In order to reduce code duplication and not require non-const duplicates of In hypothetical "const effect" syntax that could be formulated as const<X> fn bar(f: const<X> fn()) { f() }
const<X> fn bar2(f: fn()) -> fn() { f }
const<X> fn bar3(f: const<X> fn()) -> fn() { f(); f }
const<X> fn bar4(f: const<X> fn()) -> const<X> fn() { f(); f } where the use of In struct Foo {
f: fn(),
g: fn(),
}
const fn foo_new(f: fn(), g: fn()) -> Foo {
Foo { f, g }
} would naturally exist in this system. Such types have a downside though, one cannot in hindsight attach const fn exec_f(foo: Foo) {
(foo.f)() // ERROR, do not know whether `foo.f` is a const fn
} There are a few possible solutions to this: // force `g` to be `const` even though we don't need that
const fn exec_f(foo: const Foo);
// effect system
const<F> struct Foo {
f: const<F> fn(),
g: fn(), // if the library author forgot to add an effect to `g`
// you can't write a const fn to call it even if you know about it
}
const<F> exec_f(foo: Foo<F>);
// where bounds which only matter at compile-time
const fn exec_f(foo: Foo) where foo.f: const fn(); The third solution seems to be the most powerful solution, but there's no way to reason about the return value, so we'd also need some more magic like It's not all that relevant to stabilizing the use of function pointers in the "cannot call function pointers in const fn"-scheme, since that scheme is the only one consistent with what we're doing in constants right now. Any way to call const fns through functions pointers will need extra syntax, so we can "just" stabilize function pointers right now. Trait objects follow a similar reasoning trait Foo {
fn bar(&self);
}
impl Foo for () {
fn bar(&self) {}
}
const D: &Foo = &(); is legal Rust today, but there's no way to ensure that const E: () = D.bar(); would be const evaluable without changing Generics are in a similar situation. trait Foo {
fn new() -> Self;
}
const fn foo<T: Foo>() -> T {
T::new()
}
const fn foo2<T: Foo>() {
}
While we could default to "must be const fn callable", that would break the very common use case of things like |
rereading @RalfJung 's post rust-lang/const-eval#1 (comment) it seems we have come to the same conclusion |
continued from #53555 (comment) (cc @SimonSapin @Centril )
not really. we have restricted
min_const_fn
to the point where all futures are possible.Resolving inconsistencies
I'm proposing that we stabilize function pointers in const fns by not allowing calling them until a future syntax enables us to call them. Below a possible syntax is sketched to show that such a scheme is possible
Trait objects and generic trait bounds
Similar to the above I propose that one can specify arbitrary trait bounds and trait objects but not call any methods on them. Future syntax can opt into const trait objects and const trait bounds, enforcing these if the const fn is called inside a constant environment (const/static initializer, array length, enum discriminant)
The text was updated successfully, but these errors were encountered: