From 3a57a13d6a3d107d2b99bded4fc31f14e63cded9 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 20 Feb 2018 14:48:33 +0100 Subject: [PATCH 1/3] Allow `if` and `match` in constants --- text/0000-const-control-flow.md | 136 ++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 text/0000-const-control-flow.md diff --git a/text/0000-const-control-flow.md b/text/0000-const-control-flow.md new file mode 100644 index 00000000000..5bcb497908e --- /dev/null +++ b/text/0000-const-control-flow.md @@ -0,0 +1,136 @@ +- Feature Name: const-control-flow +- Start Date: 2018-01-11 +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +# Summary +[summary]: #summary + +Enable `if` and `match` during const evaluation and make them evaluate lazily. +In short, this will allow `if x < y { y - x } else { x - y }` even though the +else branch would emit an overflow error for unsigned types if `x < y`. + +# Motivation +[motivation]: #motivation + +Conditions in constants are important for making functions like `NonZero::new` +const fn and interpreting assertions. + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +If you write + +```rust +let x: u32 = ...; +let y: u32 = ...; +let a = x - y; +let b = y - x; +if x > y { + // do something with a +} else { + // do something with b +} +``` + +The program will always panic (except if both `x` and `y` are `0`) because +either `x - y` will overflow or `y - x` will. To resolve this one must move the +`let a` and `let b` into the `if` and `else` branch respectively. + +```rust +let x: u32 = ...; +let y: u32 = ...; +if x > y { + let a = x - y; + // do something with a +} else { + let b = y - x; + // do something with b +} +``` + +When constants are involved, new issues arise: + +```rust +const X: u32 = ...; +const Y: u32 = ...; +const FOO: SomeType = if X > Y { + const A: u32 = X - Y; + ... +} else { + const B: u32 = Y - X; + ... +}; +``` + +`A` and `B` are evaluated before `FOO`, since constants are by definition +constant, so their order of evaluation should not matter. This assumption breaks +in the presence of errors, because errors are side effects, and thus not pure. + +To resolve this issue, one needs to eliminate the intermediate constants and +directly evaluate `X - Y` and `Y - X`. + +```rust +const X: u32 = ...; +const Y: u32 = ...; +const FOO: SomeType = if X > Y { + let a = X - Y; + ... +} else { + let b = Y - X; + ... +}; +``` + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +Currently interpreting `switch` and `switchInt` terminators is not allowed +during mir interpretation. This RFC proposes to allow them and ignore the +branches not taken, even if they contain errors. This removes another difference +between constant evaluation and runtime execution. + +# Drawbacks +[drawbacks]: #drawbacks + +This makes it easier to fail compilation on random "constant" values like +`size_of::()` or other platform specific constants. + +# Rationale and alternatives +[alternatives]: #alternatives + +## Require intermediate const fns to break the eager const evaluation + +Instead of writing + +```rust +const X: u32 = ...; +const Y: u32 = ...; +const AB: u32 = if X > Y { + X - Y +} else { + Y - X +}; +``` + +where either `X - Y` or `Y - X` would emit an error, add an intermediate const fn + +```rust +const X: u32 = ...; +const Y: u32 = ...; +const fn foo(x: u32, y: u32) -> u32 { + if x > y { + x - y + } else { + y - x + } +} +const AB: u32 = foo(x, y); +``` + +Since the const fn's `x` and `y` arguments are unknown, they cannot be const +evaluated. When the const fn is evaluated with given arguments, only the taken +branch is evaluated. + +# Unresolved questions +[unresolved]: #unresolved-questions From e5cca9f825876ac4b78fc9ac99d94156e1cce0b8 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 20 Feb 2018 16:29:51 +0100 Subject: [PATCH 2/3] Discuss `match` in the reference level explanation --- text/0000-const-control-flow.md | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/text/0000-const-control-flow.md b/text/0000-const-control-flow.md index 5bcb497908e..321744f257f 100644 --- a/text/0000-const-control-flow.md +++ b/text/0000-const-control-flow.md @@ -85,10 +85,17 @@ const FOO: SomeType = if X > Y { # Reference-level explanation [reference-level-explanation]: #reference-level-explanation -Currently interpreting `switch` and `switchInt` terminators is not allowed -during mir interpretation. This RFC proposes to allow them and ignore the -branches not taken, even if they contain errors. This removes another difference -between constant evaluation and runtime execution. +`match` on enums whose variants have no fields or `if` is translated during HIR +-> MIR lowering to a `switchInt` terminator. Mir interpretation will now have to +evaluate those terminators (which it already can). + +`match` on enums with variants which have fields is translated to `switch`, +which will check either the discriminant or compute the discriminant in the case +of packed enums like `Option<&T>` (which has no special memory location for the +discriminant, but encodes `None` as all zeros and treats everything else as a +`Some`). When entering a `match` arm's branch, the matched on value is +essentially transmuted to the enum variant's type, allowing further code to +access its fields. # Drawbacks [drawbacks]: #drawbacks From 9976afb0b2cc548d5a105a2bab25c21cbcf44961 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Sun, 18 Mar 2018 21:47:13 +0100 Subject: [PATCH 3/3] RFC 2342 --- ...000-const-control-flow.md => 2342-const-control-flow.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename text/{0000-const-control-flow.md => 2342-const-control-flow.md} (94%) diff --git a/text/0000-const-control-flow.md b/text/2342-const-control-flow.md similarity index 94% rename from text/0000-const-control-flow.md rename to text/2342-const-control-flow.md index 321744f257f..0eaff0480af 100644 --- a/text/0000-const-control-flow.md +++ b/text/2342-const-control-flow.md @@ -1,7 +1,7 @@ -- Feature Name: const-control-flow +- Feature Name: `const-control-flow` - Start Date: 2018-01-11 -- RFC PR: (leave this empty) -- Rust Issue: (leave this empty) +- RFC PR: [rust-lang/rfcs#2342](https://github.com/rust-lang/rfcs/pull/2342) +- Rust Issue: [rust-lang/rust#49146](https://github.com/rust-lang/rust/issues/49146) # Summary [summary]: #summary