Since specialization exists on nightly in some form, I'm wondering if a similar mechanism could be used for this purpose. Essentially, having two functions with an identical signature, but one fn and another const fn. Depending on the context it is called from, compiler would pick one or the other. This allows regular function to freely use any intrinsics it wants, while const fn can use some fallback implementation.
This seems relatively clean and extends existing precedent of specialization.
Why would you prefer using specialization for this?
const_eval_select also already exists, and it has significantly fewer blocking problems than the full specialization iceberg. Namely:
Do we want functions to be allowed to do different things depending on if they're required to be const evaluated or not; and
to justify the excessive additional problematic implications this has on the type system.
Personally, I strongly believe that "expression syntax specialization" has a much stronger story than full "impl syntax specialization," but conflating "const or not specialization" with type system specialization (even calling it "const specialization") doesn't help anything.
The best way to describe it is that it doesn't look idiomatic, intuition when I see it seems backwards. So I thought what would look natural in Rust (while being oblivious to the implementation complexity) and concluded that something like specialization is a great fit.
const is kind of a separate compilation target, but the distinction is less like x86-64 vs aarch64 and more similar to fn vs async fn IMO in terms of what you can and can't do. And I would it find equally unnatural if there was something like [async] fn method with async_select inside of its implementation.
Specialization is a way to provide a more efficient implementation for a narrow subset of a more general implementation. const fn can be considered the lowest common denominator baseline that works both in const fn and regular functions. Then more efficient implementation could be used when platform-specific features are available in non-const functions.
Calling regular functions from const fn looks very counter-intuitive to me. It is unlike anything I've seen in Rust before.
Also const_eval_select is currently under compiler intrinsics, which is not something that external crates are supposed to rely on even on nightly.
This is really a bummer, but specialisation is unlikely to lead to hit stable any time in the next several years as well. It might be better to try to make public and stabilise const_eval_select instead. (All the things std can do that others can't really irks me.)
I disagree with this. Compiler intrinsics are function-like things that, for one reason or another, aren't implemented as a function. 99% of the time, there's no good reason for it to not be a function, so users should access that functionality via a wrapper function.
For const_eval_select though, this functionality requires checks that aren't currently expressible in rust trait bounds. Therefore, I would treat it mostly like normal nightly functions.
warning: the feature `core_intrinsics` is internal to the compiler or standard library
--> src/lib.rs:1:12
|
1 | #![feature(core_intrinsics)]
| ^^^^^^^^^^^^^^^
|
= note: using it is strongly discouraged
= note: `#[warn(internal_features)]` on by default
"using it is strongly discouraged" is very unambiguous.
Too add to that, per policy using intrinsics incorrectly and causing bugs (e.g. ICEs) is "you're holding it wrong, not a bug". I'm not sure but it might even be that causing unsoundness is classified like that.
Fully agreed. I don't see how specialization avoids any of the problems with const_eval_select, and in exchange it adds a couple of new ones.
I think the answer is a fairly firm "yes" to this. We should consider it an anti-pattern, like a function called get_even that returns an odd number, but it has no fundamental theoretical/opsem downsides and we don't have a reasonable way of preventing people from doing it without also excluding tons of valid use-cases or making them unnecessarily gnarly.
Yeah that is kind of the main blocker. Using the intrinsic directly is very annoying, not a good user experience at all.
Implementing a macro like you suggested on top of the intrinsic is ~impossible as the intrinsic requires an explicit capture list.
I would disagree with that. Compiler intrinsics are language primitives like + or await, they just don't need a dedicated syntax so they don't get done.
And indeed their exact shape and semantics is a private rustc implementation detail and subject to change, and it is ill-advised even for nightly code to rely on them. Hence the warning.
I'm sure things are more complicated than that, but this particular kind of specialization (identical signature except const) could probably be emulated through const_eval_select intrinsic:
rename original functions to something else
call them from new const fn with original name that uses const_eval_select to call one of the two original functions