-
Notifications
You must be signed in to change notification settings - Fork 17
Define promotion contexts and promotability #28
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
Merged
oli-obk
merged 9 commits into
rust-lang:master
from
ecstatic-morse:promotion-terminology
Oct 22, 2019
Merged
Changes from 5 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
7231ee7
Remove numbers from requirements
ecstatic-morse 4b4e01f
Define promotion in terms of "context" and "promotability"
ecstatic-morse 10543e9
Fix error regarding explicit promotion in `const`s
ecstatic-morse 0e03060
Fix link to RFC 2203
ecstatic-morse fbeb116
Better explain the motivation of RFC 2203
ecstatic-morse 3d2f41a
Fix typos
ecstatic-morse 14b0ce1
Reword description of promotability
ecstatic-morse 7d5752d
Clarify rules around promotablility of `const`s
ecstatic-morse 3ccf570
Merge sections on lifetime extension in consts and statics
ecstatic-morse File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,16 @@ | ||
# Const promotion | ||
|
||
["(Implicit) Promotion"][promotion-rfc] is a mechanism that affects code like `&3`: | ||
"Promotion" is the act of guaranteeing that code not written in a const context | ||
(e.g. initalizer of a `const` or `static`, or an array length expression) will | ||
be run at compile-time. | ||
|
||
## Promotion contexts | ||
|
||
There are a few different contexts where promotion is beneficial. | ||
|
||
### Lifetime extension | ||
|
||
"Lifetime extension" is a mechanism that affects code like `&3`: | ||
Instead of putting it on the stack, the `3` is allocated in global static memory | ||
and a reference with lifetime `'static` is provided. This is essentially an | ||
automatic transformation turning `&EXPR` into | ||
|
@@ -10,17 +20,113 @@ Note that promotion happens on the MIR, not on surface-level syntax. This is | |
relevant when discussing e.g. handling of panics caused by overflowing | ||
arithmetic. | ||
|
||
Lifetime extension is described in [RFC 1414][promotion-rfc]. The RFC uses the | ||
word "promotion" to refer exclusively to lifetime extension, since this was the | ||
first context where promotion was done. | ||
|
||
[promotion-rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1414-rvalue_static_promotion.md | ||
|
||
### Non-`Copy` array initialization | ||
|
||
Another promotion context, the initializer of an array expression, was | ||
introduced in [RFC 2203][]. Here, promotion allows arrays of | ||
non-`Copy` types to be initialized idiomatically, for example | ||
`[Option::<Box<i32>>::None; 32]`. | ||
|
||
[RFC 2203]: https://github.com/rust-lang/rfcs/blob/master/text/2203-const-repeat-expr.md | ||
|
||
### `#[rustc_args_required_const(...)]` | ||
|
||
Additionally, some platform intrinsics require certain operations to be | ||
ecstatic-morse marked this conversation as resolved.
Show resolved
Hide resolved
|
||
immediates (known at compile-time). We use the `#[rustc_args_required_const]` | ||
attribute, introduced in | ||
[rust-lang/rust#48018](https://github.com/rust-lang/rust/pull/48018), to | ||
specify these parameters and (aggressively, see below) try to promote the | ||
corresponding arguments. | ||
|
||
### Implicit and explicit contexts | ||
|
||
On top of what applies to [consts](const.md), promoteds suffer from the additional issue that *the user did not ask for them to be evaluated at compile-time*. | ||
Thus, if CTFE fails but the code would have worked fine at run-time, we broke the user's code for no good reason. | ||
Even if we are sure we found an error in the user's code, we are only allowed to [emit a warning, not a hard error][warn-rfc]. | ||
That's why we have to be very conservative with what can and cannot be promoted. | ||
|
||
[promotion-rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1414-rvalue_static_promotion.md | ||
For example, users might be surprised to learn that whenever they take a | ||
reference to a temporary, that temporary may be promoted away and never | ||
actually put on the stack. In this way, lifetime extension is an "implicit | ||
promotion context": the user did not ask for the value to be promoted. | ||
|
||
On the other hand, when a user passes an expression to a function with | ||
`#[rustc_args_required_const]`, they are explicitly asking for that expression | ||
to be evaluated at compile-time even though they have not written it in a | ||
`const` declaration. We call this an "explicit promotion context". | ||
|
||
Currently, non-`Copy` array initialization is treated as an implicit context. | ||
|
||
The distinction between these two determines whether calls to arbitrary `const | ||
fn`s (those without `#[rustc_promotable]`) are promotable (see below). See | ||
[rust-rfcs/const-eval#19](https://github.com/rust-rfcs/const-eval/issues/19) | ||
for a thorough discussion of this. At present, this is the only difference | ||
between implicit and explicit contexts. The requirements for promotion in an | ||
implicit context are a superset of the ones in an explicit context. | ||
|
||
[warn-rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1229-compile-time-asserts.md | ||
|
||
## Rules | ||
### Promotion contexts in `const` and `static` | ||
|
||
We defined above that promotion guarantees that code in a non-const context | ||
will be executed at compile-time. However, lifetime extension and non-`Copy` | ||
array initialziation are useful features *inside* `const`s and `static`s as | ||
ecstatic-morse marked this conversation as resolved.
Show resolved
Hide resolved
|
||
well. Strictly speaking, the transformation used to enable these features | ||
inside a const-context is not promotion; no `promoted`s are created in the MIR. | ||
However the same rules for promotability are used with one modification: | ||
Because the user has already requested that this code run at compile time, all | ||
contexts are treated as explicit. | ||
|
||
## Promotability | ||
|
||
We have defined when it is desirable to promote expressions but have not yet | ||
defined which expressions can actually be promoted. We refer to such | ||
expressions as "promotable". | ||
|
||
### Named locals | ||
|
||
Promotable expressions cannot refer to named locals. This is not a technical | ||
limitation with the CTFE engine. While writing `let x = {expr}` outside of a | ||
const context, the user likely expects that `x` will live on the stack and be | ||
initialized at run-time. Although this is not (to my knowledge) guaranteed by | ||
the language, we do not wish to violate the user's expectations here. | ||
|
||
### Single assignment | ||
|
||
We only promote temporaries that are assigned to exactly once. For example, the | ||
lifetime of the temporary whose reference is assigned to `x` below will not be | ||
extended. | ||
|
||
```rust | ||
let x: &'static i32 = &if cfg!(windows) { 0 } else { 1 }; | ||
``` | ||
|
||
Once again, this is not a fundamental limitation in the CTFE engine; we are | ||
perfectly capable of evaluating such expressions at compile time. However, | ||
determining the promotability of complex expressions would require more | ||
resources for little benefit. | ||
|
||
### Access to a `const` or `static` | ||
|
||
Accesses to `const`s are always promotable, regardless of the body of the | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. well, not quite "regardless". You still can't promote |
||
`const`. For instance, while the previous example was not legal, the | ||
following would be: | ||
|
||
```rust | ||
const NOT_WINDOWS: i32 = if cfg!(windows) { 0 } else { 1 }; | ||
let x: &'static i32 = &NOT_WINDOWS; | ||
``` | ||
|
||
However, an access to a `static` is only promotable within the initializer of | ||
another `static`. | ||
|
||
### 1. Panics | ||
### Panics | ||
|
||
Promotion is not allowed to throw away side effects. This includes panicking. | ||
Let us look at what happens when we promote `&(0_usize - 1)` in a debug build: | ||
|
@@ -56,7 +162,7 @@ could not panic!) at run-time leads to a compile-time CTFE error. | |
*Dynamic check.* The Miri engine already dynamically detects panics, but the | ||
main point of promoteds is ruling them out statically. | ||
|
||
### 2. Const safety | ||
### Const safety | ||
|
||
We have explained what happens when evaluating a promoted panics, but what about | ||
other kinds of failure -- what about hitting an unsupported operation or | ||
|
@@ -105,7 +211,7 @@ For this reason, only `const fn` that were explicitly marked with the | |
*Dynamic check.* The Miri engine already dynamically detects const safety | ||
violations, but the main point of promoteds is ruling them out statically. | ||
|
||
### 3. Drop | ||
### Drop | ||
|
||
Expressions returning "needs drop" types can never be promoted. If such an | ||
expression were promoted, the `Drop` impl would never get called on the value, | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are examples for code in const-context, right? Because to me this reads like examples for "code not written in a const context".