owned this note
owned this note
Published
Linked with GitHub
# Promotion (and const-error) master plan
When a CTFE query fails (except with `TooGeneric`), it should emit a hard error. That would make a *lot* of code much simpler, some of which having caused soundness problems in the past.
Achieving that goal requires two independent changes:
* Make `const_err` a hard error ([#71800](https://github.com/rust-lang/rust/issues/71800))
* Fixing promotion ([const-eval#53](https://github.com/rust-lang/const-eval/issues/53))
The former is mostly a matter of "just do it", so the rest of this document is about the latter. For context, please read [const-eval#53](https://github.com/rust-lang/const-eval/issues/53).
## Promotion for required-const args
This is easy, codegen already needs the const value and fails when these promoteds cannot be evaluted, even in dead code. Miri presumably only evaluates them on-demand, but if we could add them to `required_consts` then that would also be taken care of.
**Open question:** In `const`/`static` initializers, we could in principle be lazy and not evaluate these args unless we have to. But do we want to attempt that? If we do, we are inconsistent with these promoteds in functions, but if we do not, we are likely inconsistent with lifetime extension promoteds in `const`/`static` initializers (see below).
## Promotion for lifetime extension in `fn` and `const fn`
These are codegen'd, so we have to evaluate all promoteds, even in dead code.
### `fn`
Possibly fallible things that we promote:
* Division, Modulo -- can we promote them only when the RHS is a non-0 const?
* Array/slice indexing -- can we promote only array indexing with a const index (so the bounds check is entirely static)?
Note that other arithmetic is infallible even in debug builds where overflows panic -- what is actually getting promoted is the CheckedAdd that returns the wrapped result and whether an overflow happens. That operation never fails.
### `const fn`
Accidentally, we promote way more in `const fn` than in `fn`. [#75502](https://github.com/rust-lang/rust/pull/75502) is exploring if we can sort that out. Ideally, `const fn` would be treated exactly like `fn` for the purpose of lifetime extension. If that is not possible... it is unclear how much of this plan can be salvaged.
## Promotion for lifetime extension in `const`/`static` initializers
We currently do a *lot* of lifetime extension in these contexts (almost every closed expression of the form `&expr`), and we do it via promotion. It is unlikely that we will be able to do much less lifetime extension (for backwards compatibility reasons).
This would introduce at least two special cases:
* These would be the only promoteds whose evaluation could fail -- so to achieve the master plan, we have to make sure we only evaluate them when needed. This means MIR optimizations need to be not run, or very careful. (Some MIR optimizations do linting, but do we even need these lints here?)
* Inside `static mut`, we do lifetime extension for things like `&mut [1,2,3]`. This would be the only promoteds whose result needs to be put into mutable memory. (Right now, this works mostly by accident.) This feature is [incompatible with mutable references in const/static bodies](https://github.com/rust-lang/rust/issues/75556).
Unfortunately, the alternative of doing lifetime extension via a different mechanism (`StorageDead` removal) does not work when there are loops in the initializer.