From dfcde457be3606e157f41beab9a032d50b1e5413 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 15 Nov 2020 13:38:16 +0100 Subject: [PATCH 1/8] make implicit promotion infallible --- text/0000-infallible-promotion.md | 166 ++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 text/0000-infallible-promotion.md diff --git a/text/0000-infallible-promotion.md b/text/0000-infallible-promotion.md new file mode 100644 index 00000000000..a5ba4e1803a --- /dev/null +++ b/text/0000-infallible-promotion.md @@ -0,0 +1,166 @@ +- Feature Name: infallible_lifetime_extension +- Start Date: 2020-11-08 +- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) +- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) + +# Summary +[summary]: #summary + +Restrict (implicit) [promotion][rfc1414], such as lifetime extension of rvalues, to infallible operations. + +[rfc1414]: 1414-rvalue_static_promotion.md + +# Motivation +[motivation]: #motivation + +## Background on promotion and lifetime extension + +Rvalue promotion (as it was originally called) describes the process of taking an rvalue that can be computed at compile-time, and "promoting" it to a constant, so that references to that rvalue can have `'static` lifetime. +It has been introduced by [RFC 1414][rfc1414]. +The scope of what exactly is being promoted in which context has been extended over the years in an ad-hoc manner, and the underlying mechanism of promotion (to extract a part of a larger body of code into a separate constant) is now also used for purposes other than making references have `'statc` lifetime. +To account for this, the const-eval WG [agreed on the following terminology][promotion-status]: +* Making references have `'static` lifetime is called "lifetime extension". +* The underlying mechanism of extracting part of some code into a constant is called "promotion". + +Promotion is currently used for four compiler features: +* lifetime extension +* non-`Copy` array repeat expressions +* functions where some arguments must be known at compile-time (`#[rustc_args_required_const]`) +* `const` operands of `asm!` + +These uses of promotion fall into two categories: +* *Explicit* promotion refers to promotion where not promoting is simply not an option: `#[rustc_args_required_const]` and `asm!` *require* the value of this expression to be known at compile-time. +* *Implicit* promotion refers to promotion that might not be required: a reference might not actually need to have `'static` lifetime, and an array repeat expression could be `Copy` (or the repeat count no larger than 1). + +For more details, see the [const-eval WG writeup][promotion-status]. + +## The problem with implicit promotion + +Explicit promotion is mostly fine as-is. +This RFC is concerned with implicit promotion. +The problem with implicit promotion is best demonstrated by the following example: + +```rust +fn make_something() { + if false { &(1/0) } +} +``` + +If the compiler decides to do implicit promotion here, the code is changed to something like + +```rust +fn make_something() { + if false { + const VAL: &i32 = &(1/0); + VAL + } +} +``` + +However, this code would fail to compile! +When doing code generation for a function, all its constants have to be evaluated, including the ones in dead code, since in general we cannot know that we are compiling dead code. +(In fact, there is even code that [relies on failing constants stopping compilation](https://github.com/rust-lang/rust/issues/67191).) +When evaluating `VAL`, a panic is triggered due to division by zero, so any code that needs to know the value of `VAL` is stuck as there is no such value. + +This is a problem because the original code (pre-promotion) works just fine: the division never actually happens. +It is only because the compiler decided to extract the division into a separately evaluated constant that it even becomes a problem. +Notice that this is a problem only for implicit promotion, because with explicit promotion, the value *has* to be known at compile-time -- so stopping compilation if the value cannot be determined is the right behavior. + +To solve this problem, every part of the compiler that works with constants needs to be able to handle the case where the constant *has no defined value*, and continue in some correct way. +This is hard to get right, and has lead to a number of problems over the years: +* There has been at least one [soundness issue](https://github.com/rust-lang/rust/issues/50814). +* There are still outstanding [diagnostic issues](https://github.com/rust-lang/rust/issues/61821). +* Promotion needs a special [exception in const-value validation](https://github.com/rust-lang/rust/issues/67534). +* All code handling constants has to carry [extra complexity to support promotion](https://github.com/rust-lang/rust/issues/75461) + +This RFC proposes to fix all these problems at once, by restricting implicit promotion to those expression whose evaluation cannot fail. +This is the last step in a series of changes that have been going on for quite some time, starting with the [introduction](https://github.com/rust-lang/rust/pull/53851) of the `#[rustc_promotable]` attribute to control which function calls may be subject to implicit promotion (the original RFC said that all calls to `const fn` should be promoted, but as user-defined `const fn` got closer and closer, that seemed less and less like a good idea, due to all the ways in which evaluating a `const fn` can fail). +Together with [some planned changes for evaluation of regular constants](https://github.com/rust-lang/rust/issues/71800), this means that all CTFE failures can be made hard errors, greatly simplifying the parts of the compiler that trigger evaluation of constants and handle the resulting value or error. + +[promotion-status]: https://github.com/rust-lang/const-eval/blob/33053bb2c9a0c6a17acd3116dd47bbb360e060db/promotion.md + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +(Based on [RFC 1414][rfc1414]) + +Inside a function body's block: + +- If a shared reference to a constexpr rvalue is taken. (`&`) +- And the constexpr does not contain a `UnsafeCell { ... }` constructor. +- And the constexpr only consists of operations that will definitely succeed to + evaluate at compile-time +- Then instead of translating the value into a stack slot, translate + it into a static memory location and give the resulting reference a + `'static` lifetime. + +Operations that definitely succeed include literals of any kind, constructors (struct/enum/union/tuple), struct/tuple field accesses, `+`/`-`/`*` (even with overflow checks, the underlying `Checked*` MIR operation will not fail). +Operations that might fail include `/`/`%`, `panic!` (including the assertion that follows `Checked*` arithmetic to ensure that no overflow happened), array/slice indexing, union field accesses, and `const fn` calls (as they might do any of the above). + +Compared to the status quo, this means the following expressions are not implicitly promoted any more: +* Division, modulo, array/slice indexing +* `const fn` calls in `const`/`static` bodies (`const fn` are already not being implicitly promoted in `fn` and `const fn` bodies) + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +TODO: repeat everything in the guide-level explanation? That makes little sense; not sure what the template asks from me here. + +# Drawbacks +[drawbacks]: #drawbacks + +The biggest drawback is that this will break some existing code: if code relies on implicit promotion of potentially fallible operations, it will stop to compile. +Crater runs should be used all along the way to ensure that the fall-out is acceptable. +The language team will be involved in each breaking change to make this judgment call. +If too much code is broken, various ways to weaken this proposal (at the expense of more technical debt, sometimes across several parts of the compiler) are [described blow][rationale-and-alternatives]. + +The long-term plan is that such code can switch to [inline `const` expressions](2920-inline-const.md) instead. +However, inline `const` expressions are still in the process of being implemented, and for now are specified to not support code that depends on generic parameters in the context, which is a loss of expressivity when compared with implicit promotion. +More complex work-around are possible for this using associated `const`, but they can become quite tedious. + +# Rationale and alternatives +[rationale-and-alternatives]: #rationale-and-alternatives + +The rationale has been described with the motivation. + +Unless we want to keep supporting fallible const-evaluation indefinitely, the main alternatives are devising more precise analyses to determine if some operation is infallible. +For example, we could still perform implicit promotion for division and modulo if the divisor is a non-zero constant. +We could still perform *array* indexing if the index is a constant and in-bounds. +For slices, we could have an analysis that predicts the (minimum) length of the slice. +Notice that promotion happens in generic code and can depend on associated constants, so we cannot, in general, *evaluate* the implicit promotion candidate to check if that causes any errors. + +We could also decide to still perform implicit promotion of potentially fallible operations in the bodies of `const`s and `static`s. +(This would mean that the RFC only changes behavior of implicit promotion in `fn` and `const fn` bodies.) +This is possible because that code is not subject to code generation, it is only interpreted by the CTFE engine. +The engine will only evaluate the part of the code that is actually being run, and thus can avoid evaluating promoteds in dead code. +However, this means that all other consumers of this code (such as pretty-printing and optimizations) must *not* evaluate promoteds that they encounter, since that evaluation may fail. +This will incur technical debt in all of those places, as we need to carefully ensure not to eagerly evaluate all constants that we encounter. +We also need to be careful to still evaluate all user-defined constants even inside promoteds in dead code (because, remember, code may rely on the fact that compilation will fail if any constant that is syntactically used in a function fails to evaluated). +Note that this is *not* an option for code generation, i.e., for code in `fn` and `const fn`: all code needs to be translated to LLVM, even possibly dead code, so we have to evaluate all constants that we encounter. + +If there are some standard library `const fn` that cannot fail to evaluate, and that form the bulk of the function calls being implicitly promoted, we could add the `#[rustc_promotable]` attribute to them to enable implicit promotion. +This will not help, however, if there is plenty of code relying on implicit promotion of user-defined `const fn`. + +# Prior art +[prior-art]: #prior-art + +A few changes have landed in the recent past that already move us, step-by-step, towards the goal outlined in this RFC: +* Treat `const fn` like `fn` for promotability: https://github.com/rust-lang/rust/pull/75502, https://github.com/rust-lang/rust/pull/76411 +* Do not promote `union` field accesses: https://github.com/rust-lang/rust/pull/77526 + +# Unresolved questions +[unresolved-questions]: #unresolved-questions + +The main open question is to what extend existing code relies on lifetime extension of fallible operations, i.e., if we can get away with the plan outlined here. +(Lifetime extension is currently the only stable form of implicit promotion, and thus the only one relevant for backwards compatibility.) +In `fn` and `const fn`, only a few fallible operations remain: division, modulo, and slice/array indexing. +In `const` and `static`, we additionally promote calls to arbitrary `const fn`, which of course could fail in arbitrary ways -- crater experiments will have to show if code actually relies on this. +A fall-back plan in case this RFC would break too much code has been [described above][rationale-and-alternatives]. + +# Future possibilities +[future-possibilities]: #future-possibilities + +A potential next step after this RFC could be to tackle the remaining main promotion "hack", the `#[rustc_promotable]` attribute. +We now know exactly what this attribute expresses: this `const fn` may never fail to evaluate (in particular, it may not panic). +This provides a theoretical path to stabilization of this attribute, backed by an analysis that ensures that the function indeed does not panic. +(However, once inline `const` expressions with generic parameters are stable, this does not actually grant any extra expressivity, just a slight increase in convenience.) From 5f08ece168a030a7df227556f354027bd5dcb95d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 15 Nov 2020 14:28:11 +0100 Subject: [PATCH 2/8] add some info that was in the MCP but not here; link to MCP --- text/0000-infallible-promotion.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/text/0000-infallible-promotion.md b/text/0000-infallible-promotion.md index a5ba4e1803a..1cd005f19f7 100644 --- a/text/0000-infallible-promotion.md +++ b/text/0000-infallible-promotion.md @@ -77,6 +77,8 @@ This RFC proposes to fix all these problems at once, by restricting implicit pro This is the last step in a series of changes that have been going on for quite some time, starting with the [introduction](https://github.com/rust-lang/rust/pull/53851) of the `#[rustc_promotable]` attribute to control which function calls may be subject to implicit promotion (the original RFC said that all calls to `const fn` should be promoted, but as user-defined `const fn` got closer and closer, that seemed less and less like a good idea, due to all the ways in which evaluating a `const fn` can fail). Together with [some planned changes for evaluation of regular constants](https://github.com/rust-lang/rust/issues/71800), this means that all CTFE failures can be made hard errors, greatly simplifying the parts of the compiler that trigger evaluation of constants and handle the resulting value or error. +For more details, see [the MCP that preceded this RFC](https://github.com/rust-lang/lang-team/issues/58). + [promotion-status]: https://github.com/rust-lang/const-eval/blob/33053bb2c9a0c6a17acd3116dd47bbb360e060db/promotion.md # Guide-level explanation @@ -94,7 +96,8 @@ Inside a function body's block: it into a static memory location and give the resulting reference a `'static` lifetime. -Operations that definitely succeed include literals of any kind, constructors (struct/enum/union/tuple), struct/tuple field accesses, `+`/`-`/`*` (even with overflow checks, the underlying `Checked*` MIR operation will not fail). +Operations that definitely succeed include literals of any kind, constructors (struct/enum/union/tuple), struct/tuple field accesses, `+`/`-`/`*`. +(Checked arithmetic is not a problem: an addition in debug mode is compiled to a `CheckedAdd` MIR operation that never fails, which returns an `(, bool)`, and is followed by a check of said `bool` to possibly raise a panic. We only ever promote the `CheckedAdd`, so evaluation of the promoted will never fail, even if the operation overflows.) Operations that might fail include `/`/`%`, `panic!` (including the assertion that follows `Checked*` arithmetic to ensure that no overflow happened), array/slice indexing, union field accesses, and `const fn` calls (as they might do any of the above). Compared to the status quo, this means the following expressions are not implicitly promoted any more: @@ -125,6 +128,7 @@ The rationale has been described with the motivation. Unless we want to keep supporting fallible const-evaluation indefinitely, the main alternatives are devising more precise analyses to determine if some operation is infallible. For example, we could still perform implicit promotion for division and modulo if the divisor is a non-zero constant. +We could also have `CheckedDiv` and `CheckedMod` operations that, similar to operations like `CheckedAdd`, always returns a result of the right type together with a `bool` saying if the result is valid. We could still perform *array* indexing if the index is a constant and in-bounds. For slices, we could have an analysis that predicts the (minimum) length of the slice. Notice that promotion happens in generic code and can depend on associated constants, so we cannot, in general, *evaluate* the implicit promotion candidate to check if that causes any errors. @@ -141,6 +145,10 @@ Note that this is *not* an option for code generation, i.e., for code in `fn` an If there are some standard library `const fn` that cannot fail to evaluate, and that form the bulk of the function calls being implicitly promoted, we could add the `#[rustc_promotable]` attribute to them to enable implicit promotion. This will not help, however, if there is plenty of code relying on implicit promotion of user-defined `const fn`. +Conversely, if this plan all works out, one alternative proposal that goes even further is to restrict implicit promotion to expressions that would be permitted in a pattern. +This would avoid adding a new class of expression in between "patterns" and "const-evaluable". +On the other hand, it is much more restrictive (basically allowing only literals and constructors), and does not actually help simplify the compiler. + # Prior art [prior-art]: #prior-art From 575bfac8977bfb3373f565f7bf0b88348e376aba Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 28 Nov 2020 17:55:06 +0100 Subject: [PATCH 3/8] incorporate some feedback --- text/0000-infallible-promotion.md | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/text/0000-infallible-promotion.md b/text/0000-infallible-promotion.md index 1cd005f19f7..4fb25b13d90 100644 --- a/text/0000-infallible-promotion.md +++ b/text/0000-infallible-promotion.md @@ -88,33 +88,36 @@ For more details, see [the MCP that preceded this RFC](https://github.com/rust-l Inside a function body's block: -- If a shared reference to a constexpr rvalue is taken. (`&`) -- And the constexpr does not contain a `UnsafeCell { ... }` constructor. +- If a shared reference to a constexpr rvalue is taken. (`&`), +- And the constexpr does not contain a `UnsafeCell { ... }` constructor, - And the constexpr only consists of operations that will definitely succeed to - evaluate at compile-time + evaluate at compile-time, +- And the resulting value does not need dropping, - Then instead of translating the value into a stack slot, translate it into a static memory location and give the resulting reference a `'static` lifetime. Operations that definitely succeed include literals of any kind, constructors (struct/enum/union/tuple), struct/tuple field accesses, `+`/`-`/`*`. (Checked arithmetic is not a problem: an addition in debug mode is compiled to a `CheckedAdd` MIR operation that never fails, which returns an `(, bool)`, and is followed by a check of said `bool` to possibly raise a panic. We only ever promote the `CheckedAdd`, so evaluation of the promoted will never fail, even if the operation overflows.) -Operations that might fail include `/`/`%`, `panic!` (including the assertion that follows `Checked*` arithmetic to ensure that no overflow happened), array/slice indexing, union field accesses, and `const fn` calls (as they might do any of the above). - -Compared to the status quo, this means the following expressions are not implicitly promoted any more: -* Division, modulo, array/slice indexing -* `const fn` calls in `const`/`static` bodies (`const fn` are already not being implicitly promoted in `fn` and `const fn` bodies) +Operations that might fail include `/`/`%`, `panic!` (including the assertion that follows `Checked*` arithmetic to ensure that no overflow happened), array/slice indexing, any unsafe operation, and `const fn` calls (as they might do any of the above). # Reference-level explanation [reference-level-explanation]: #reference-level-explanation -TODO: repeat everything in the guide-level explanation? That makes little sense; not sure what the template asks from me here. +See above for (hopefully) all the required details. +What exactly the rules will end up being for which operations can be promoted will depend on experimentation to avoid breaking too much existing code, as discussed below. # Drawbacks [drawbacks]: #drawbacks -The biggest drawback is that this will break some existing code: if code relies on implicit promotion of potentially fallible operations, it will stop to compile. +The biggest drawback is that this will break some existing code. +Compared to the status quo, this means the following expressions are not implicitly promoted any more: +* Division, modulo, array/slice indexing +* `const fn` calls in `const`/`static` bodies (`const fn` are already not being implicitly promoted in `fn` and `const fn` bodies) + +If code relies on implicit promotion of these operations, it will stop to compile. Crater runs should be used all along the way to ensure that the fall-out is acceptable. -The language team will be involved in each breaking change to make this judgment call. +The language team will be involved (via FCP) in each breaking change to make this judgment call. If too much code is broken, various ways to weaken this proposal (at the expense of more technical debt, sometimes across several parts of the compiler) are [described blow][rationale-and-alternatives]. The long-term plan is that such code can switch to [inline `const` expressions](2920-inline-const.md) instead. @@ -127,7 +130,7 @@ More complex work-around are possible for this using associated `const`, but the The rationale has been described with the motivation. Unless we want to keep supporting fallible const-evaluation indefinitely, the main alternatives are devising more precise analyses to determine if some operation is infallible. -For example, we could still perform implicit promotion for division and modulo if the divisor is a non-zero constant. +For example, we could still perform implicit promotion for division and modulo if the divisor is a non-zero literal. We could also have `CheckedDiv` and `CheckedMod` operations that, similar to operations like `CheckedAdd`, always returns a result of the right type together with a `bool` saying if the result is valid. We could still perform *array* indexing if the index is a constant and in-bounds. For slices, we could have an analysis that predicts the (minimum) length of the slice. From 4592892c0b2abe8474871a4bcc304f2a2d0ca7a1 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 28 Nov 2020 18:12:29 +0100 Subject: [PATCH 4/8] use bulleted lists and add example for CheckedAdd promotion --- text/0000-infallible-promotion.md | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/text/0000-infallible-promotion.md b/text/0000-infallible-promotion.md index 4fb25b13d90..fad1bc466c3 100644 --- a/text/0000-infallible-promotion.md +++ b/text/0000-infallible-promotion.md @@ -97,9 +97,25 @@ Inside a function body's block: it into a static memory location and give the resulting reference a `'static` lifetime. -Operations that definitely succeed include literals of any kind, constructors (struct/enum/union/tuple), struct/tuple field accesses, `+`/`-`/`*`. -(Checked arithmetic is not a problem: an addition in debug mode is compiled to a `CheckedAdd` MIR operation that never fails, which returns an `(, bool)`, and is followed by a check of said `bool` to possibly raise a panic. We only ever promote the `CheckedAdd`, so evaluation of the promoted will never fail, even if the operation overflows.) -Operations that might fail include `/`/`%`, `panic!` (including the assertion that follows `Checked*` arithmetic to ensure that no overflow happened), array/slice indexing, any unsafe operation, and `const fn` calls (as they might do any of the above). +Operations that definitely succeed include: +- literals of any kind +- constructors (struct/enum/union/tuple) +- struct/tuple field accesses +- arithmetic that does not involve division: `+`/`-`/`*` + +Note that arithmetic overflow is not a problem: an addition in debug mode is compiled to a `CheckedAdd` MIR operation that never fails, which returns an `(, bool)`, and is followed by a check of said `bool` to possibly raise a panic. We only ever promote the `CheckedAdd`, so evaluation of the promoted will never fail, even if the operation overflows. For example, `&(1 + u32::MAX)` turns into something like: +```rust +const C: (u32, bool) = CheckedAdd(1, u32::MAX); // evaluates to (1, true). +assert!(C.1 == false); +&C.0 +``` + +Operations that might fail include: +- `/`/`%` +- `panic!` (including the assertion that follows `Checked*` arithmetic to ensure that no overflow happened) +- array/slice indexing +- any unsafe operation +- `const fn` calls (as they might do any of the above) # Reference-level explanation [reference-level-explanation]: #reference-level-explanation From 49b6d56fff26c97a71070dcd7b1b00a84105646a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 2 Dec 2020 14:06:43 +0100 Subject: [PATCH 5/8] add MIR overflow check reference; mention bitwise and shift operators --- text/0000-infallible-promotion.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/text/0000-infallible-promotion.md b/text/0000-infallible-promotion.md index fad1bc466c3..751cb261bd7 100644 --- a/text/0000-infallible-promotion.md +++ b/text/0000-infallible-promotion.md @@ -8,7 +8,7 @@ Restrict (implicit) [promotion][rfc1414], such as lifetime extension of rvalues, to infallible operations. -[rfc1414]: 1414-rvalue_static_promotion.md +[rfc1414]: https://github.com/rust-lang/rfcs/blob/master/text/1414-rvalue_static_promotion.md # Motivation [motivation]: #motivation @@ -97,18 +97,21 @@ Inside a function body's block: it into a static memory location and give the resulting reference a `'static` lifetime. -Operations that definitely succeed include: +Operations that definitely succeed at the time of writing the RFC include: - literals of any kind - constructors (struct/enum/union/tuple) - struct/tuple field accesses -- arithmetic that does not involve division: `+`/`-`/`*` +- arithmetic and logical operators that do not involve division: `+`/`-`/`*`, all bitwise and shift operators, all unary operators -Note that arithmetic overflow is not a problem: an addition in debug mode is compiled to a `CheckedAdd` MIR operation that never fails, which returns an `(, bool)`, and is followed by a check of said `bool` to possibly raise a panic. We only ever promote the `CheckedAdd`, so evaluation of the promoted will never fail, even if the operation overflows. For example, `&(1 + u32::MAX)` turns into something like: +Note that arithmetic overflow is not a problem: an addition in debug mode is compiled to a `CheckedAdd` MIR operation that never fails, which returns an `(, bool)`, and is followed by a check of said `bool` to possibly raise a panic. +We only ever promote the `CheckedAdd`, so evaluation of the promoted will never fail, even if the operation overflows. +For example, `&(1 + u32::MAX)` turns into something like: ```rust const C: (u32, bool) = CheckedAdd(1, u32::MAX); // evaluates to (1, true). assert!(C.1 == false); &C.0 ``` +See [this prior RFC](https://github.com/rust-lang/rfcs/blob/master/text/1211-mir.md#overflow-checking) for further details. Operations that might fail include: - `/`/`%` From 508ae47011445ccf0a4301214fea8185ef6b5d8d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 20 Dec 2020 13:23:48 +0100 Subject: [PATCH 6/8] add note on the interaction of promotion and const validity checks --- text/0000-infallible-promotion.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/text/0000-infallible-promotion.md b/text/0000-infallible-promotion.md index 751cb261bd7..7559c48355d 100644 --- a/text/0000-infallible-promotion.md +++ b/text/0000-infallible-promotion.md @@ -113,6 +113,16 @@ assert!(C.1 == false); ``` See [this prior RFC](https://github.com/rust-lang/rfcs/blob/master/text/1211-mir.md#overflow-checking) for further details. +However, also note that operators being infallible is more subtle than it might seem. +In particular, it requires that all constants of integer type (and even all integer-typed fields of all constants) be proper integers, not pointers cast to integers. +The following code shows a problematic example: +```rust +const FOO: usize = &42 as *const i32 as usize; +let x: &usize = &(FOO * 3); +``` +`FOO*3` cannot be evaluated during CTFE, so to ensure that multiplication is infallible, we need to ensure that all constants used in promotion are proper integers. +This is currently ensured by the "validity check" that is performed on the final value of each constant: the check recursively traverses the type of the constant and ensures that the data matches that type. + Operations that might fail include: - `/`/`%` - `panic!` (including the assertion that follows `Checked*` arithmetic to ensure that no overflow happened) @@ -120,6 +130,10 @@ Operations that might fail include: - any unsafe operation - `const fn` calls (as they might do any of the above) +Notably absent from *both* of the above list is dereferencing a reference. +This operation is, in principle, infallible---but due to the concern mentioned above about validity of consts, it is only infallible if the validity check in constants traverses through references. +Currently, the check stops when hitting a reference to a static, so currently, dereferencing a reference can *not* be considered an infallible operation for the purpose of promotion. + # Reference-level explanation [reference-level-explanation]: #reference-level-explanation From 4d747ee5f6847d33e5fd197f99f61137471bdefc Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 21 Dec 2020 14:15:32 +0100 Subject: [PATCH 7/8] fix CheckedAdd example --- text/0000-infallible-promotion.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-infallible-promotion.md b/text/0000-infallible-promotion.md index 7559c48355d..a1573f30057 100644 --- a/text/0000-infallible-promotion.md +++ b/text/0000-infallible-promotion.md @@ -107,7 +107,7 @@ Note that arithmetic overflow is not a problem: an addition in debug mode is com We only ever promote the `CheckedAdd`, so evaluation of the promoted will never fail, even if the operation overflows. For example, `&(1 + u32::MAX)` turns into something like: ```rust -const C: (u32, bool) = CheckedAdd(1, u32::MAX); // evaluates to (1, true). +const C: (u32, bool) = CheckedAdd(1, u32::MAX); // evaluates to (0, true). assert!(C.1 == false); &C.0 ``` From cb78c70ba32aee9847fa5411635070367717af28 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Wed, 13 Jan 2021 14:06:30 +1000 Subject: [PATCH 8/8] RFC 3027: Infallible Promition --- ...0-infallible-promotion.md => 3027-infallible-promotion.md} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename text/{0000-infallible-promotion.md => 3027-infallible-promotion.md} (98%) diff --git a/text/0000-infallible-promotion.md b/text/3027-infallible-promotion.md similarity index 98% rename from text/0000-infallible-promotion.md rename to text/3027-infallible-promotion.md index a1573f30057..293372f49d9 100644 --- a/text/0000-infallible-promotion.md +++ b/text/3027-infallible-promotion.md @@ -1,7 +1,7 @@ - Feature Name: infallible_lifetime_extension - Start Date: 2020-11-08 -- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) -- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) +- RFC PR: [rust-lang/rfcs#3027](https://github.com/rust-lang/rfcs/pull/3027) +- Rust Issue: [rust-lang/rust#80619](https://github.com/rust-lang/rust/issues/80619) # Summary [summary]: #summary