From d364261e02ec7ce5323c97d7e3dc6e2a1fb28a53 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Mon, 4 May 2020 11:39:33 -0700 Subject: [PATCH 01/19] Inline `const` --- text/0000-inline-const.md | 281 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 281 insertions(+) create mode 100644 text/0000-inline-const.md diff --git a/text/0000-inline-const.md b/text/0000-inline-const.md new file mode 100644 index 00000000000..b0f9bebfabf --- /dev/null +++ b/text/0000-inline-const.md @@ -0,0 +1,281 @@ +- Feature Name: `inline_const` +- Start Date: 2020-04-30 +- RFC PR: TBD +- Rust Issue: TBD + +# Summary +[summary]: #summary + +Adds a new syntactical element called an "inline `const`", written as +`const { ... }`, which instructs the compiler to execute the contents of the +block at compile-time. An inline `const` can be used as an expression or +anywhere in a pattern where a named `const` would be allowed. + +```rust +use std::net::Ipv6Addr; + +fn mock_ip(use_localhost: bool) -> &'static Ipv6Addr { + if use_localhost { + &Ipv6Addr::LOCALHOST + } else { + const { &Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0) } + } +} + +fn main() { + match *x { + 0 ..= const { u32::MAX / 2 } => println!("low"), + const { u32::MAX / 2 + 1 } ..= u32::MAX => println!("high"), + } +} +``` + +# Motivation +[motivation]: #motivation + +Rust has `const` items, which are guaranteed to be initialized at compile-time. +Because of this, they can do things that normal variables cannot. For example, +a reference in a `const` initializer has the `'static` lifetime, and a `const` +can be used as an array initializer even if the type of the array is not +`Copy` (with [RFC 2203]). + +[RFC 2203]: https://github.com/rust-lang/rfcs/pull/2203 + +```rust +fn foo(x: &i32) -> &i32 { + const ZERO: &'static i32 = &0; + if *x < 0 { ZERO } else { x } +} + + +fn foo() -> &u32 { + const RANGE: Range = 0..5; // `Range` is not `Copy` + let three_ranges = [RANGE; 3]; +} +``` + +Writing out a `const` declaration everytime we need a long-lived reference or +a non-`Copy` array initializer can be annoying. To improve the situation, +[RFC 1414] introduced rvalue static promotion to extend lifetimes, and +[RFC 2203] extended the concept of promotion to array initializers. +As a result, the previous example can be written more concisely. + +[RFC 1414]: https://github.com/rust-lang/rfcs/pull/2203 + +```rust +fn foo(x: &i32) -> &i32 { + if *x < 0 { &0 } else { x } +} + +fn foo() -> &u32 { + let three_ranges = [0..5; 3]; +} +``` + +However, the fact that we are executing the array initializer or expression +after the `&` at compile-time is not obvious to the user. To avoid violating +their assumptions, we are very careful to promote only in cases where the user +cannot possibly tell that their code is not executing at runtime. This means +[long list of rules][prom-rules] for determining the promotability of expressions, and it +means expressions that call a `const fn` or that result in a type with a `Drop` +impl need to use a named `const` declaration. + +[prom-rules]: https://github.com/rust-lang/const-eval/blob/master/promotion.md#promotability + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +This proposal is a middle ground, which is less verbose than named constants but +more obvious and expressive than promotion. + +```rust +fn foo(x: &i32) -> &i32 { + if *x < 0 { const { &4i32.pow(4) } } else { x } +} + +fn foo() -> &u32 {0..5 + let three_ranges = [const { (0..=5).into_inner() }; 3]; +} +``` + +With this extension to the language, users can ensure that their code executes +at compile-time without needing to declare a separate `const` item that is only +used once. + +## Patterns + +Patterns are another context that require a named `const` when using complex +expressions. Unlike in the expression context, where promotion is sometimes +applicable, there is no other choice here. + +```rust +fn foo(x: i32) { + const CUBE: i32 = 3.pow(3); + match x { + CUBE => println!("three cubed"), + _ => {} + } +} +``` + +If that `const` is only used inside a single pattern, writing the code using an +inline `const` block makes it easier to scan. + +```rust +fn foo(x: i32) { + match x { + const { 3.pow(3) } => println!("three cubed"), + _ => {} + } +} +``` + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +This RFC extends the [grammar for expressions] to be, + +[grammar for expressions]: https://doc.rust-lang.org/stable/reference/expressions.html#expressions + +> ``` +> ExpressionWithBlock : +> OuterAttribute*† +> ( +> BlockExpression +> | AsyncBlockExpression +> | UnsafeBlockExpression +> | ConstBlockExpression // new +> | LoopExpression +> | IfExpression +> | IfLetExpression +> | MatchExpression +> ) +> +> ConstBlockExpression: `const` BlockExpression // new +> ``` + +This RFC extends the [grammar for patterns] to be, + +[grammar for patterns]: https://doc.rust-lang.org/stable/reference/patterns.html + +> ``` +> Pattern : +> LiteralPattern +> | IdentifierPattern +> | WildcardPattern +> | RangePattern +> | ReferencePattern +> | StructPattern +> | TupleStructPattern +> | TuplePattern +> | GroupedPattern +> | SlicePattern +> | PathPattern +> | MacroInvocation +> | ConstBlockExpression // new +> +> RangePatternBound : +> CHAR_LITERAL +> | BYTE_LITERAL +> | -? INTEGER_LITERAL +> | -? FLOAT_LITERAL +> | PathInExpression +> | QualifiedPathInExpression +> | ConstBlockExpression // new +> +> PathPattern : +> PathInExpression +> | QualifiedPathInExpression +> | ConstBlockExpression // new +> ``` + +In both the expression and pattern context, an inline `const` behaves exactly +as if the user had declared a uniquely identified `const` with the block's +contents as its initializer. For example, in expression context, writing +`const { ... }` is equivalent to writing: + +```rust +{ const UNIQUE_IDENT: Ty = ...; UNIQUE_IDENT } +``` + +where `Ty` is inferred from the expression inside the braces. + +An inline `const` is eligible for promotion in an implicit context (just like a +named `const`), so the following are all guaranteed to work: + +```rust +let x: &'static i32 = &const { 4i32.pow(4) }; // NOT IDIOMATIC +let x: &'static i32 = const { &4i32.pow(4) }; // IDIOMATIC + +// If RFC 2203 is stabilized +let v = [const { Vec::new() }; 3]; // IDIOMATIC +let v = const { [ Vec::new(); 3] }; // NOT IDIOMATIC +``` + +Whether to lint against the non idiomatic versions is an open question. +Personally, I would like to lint against `&const {...}` but not `const { [expr; +3] }`. + +Inline `const`s are allowed within `const` and `static` initializers, just as we +currently allow nested `const` declarations. Whether to lint against inline +`const` expressions inside a `const` or `static` is also an open question. + +# Drawbacks +[drawbacks]: #drawbacks + +This excludes other uses of the `const` keyword in expressions and patterns. +I'm not aware of any other proposals that would take advantage of this. + +This would also be the first use of type inference for const initializers. I'm +not aware of any technical issues that would arise from this, but perhaps I'm +overlooking something? + +# Rationale and alternatives +[rationale-and-alternatives]: #rationale-and-alternatives + +The main alternative is the status quo. Maintaining it will likely result in +promotion being used for more contexts. The lang-team decided to [explore this +approach](https://github.com/rust-lang/rust/pull/70042#issuecomment-612221597) +instead. + +It would also possible to separate out the parts of this RFC relating to patterns +so that they can be decided upon seperately. I think they are similar enough +that they are best considered as a unit, however. + +# Prior art +[prior-art]: #prior-art + +I'm not aware of equivalents in other languages. + +AFAIK, this was [first proposed] by @scottmcm. + +[first proposed]: https://internals.rust-lang.org/t/quick-thought-const-blocks/7803/9 + +# Unresolved questions +[unresolved-questions]: #unresolved-questions + +## Naming + +I prefer the name inline `const`, since it signals that there is no difference +between a named `const` and an inline one. @scottmcm prefers "`const` block", +which is closer to the syntax and parallels the current terminology of `async` +block and `unsafe` block. It also avoids any accidental conflation with the +`#[inline]` attribute, which is unrelated. + +## Lints + +As mentioned in the reference-level specification, we need to decide whether we +want to lint against certain types of inline `const` expressions. + +# Future possibilities +[future-possibilities]: #future-possibilities + +It would be possible to allow the syntax `const expr` for an inline `const` that +consists of a single expression. This is analagous to the single expression +variant of closures: `|| 42`. This is backwards compatible with the current proposal. + +This could allow us to deprecate the more esoteric classes of promotable +expressions (e.g., `&(u32::MAX + u32::MAX)`) in favor of inline `const` +expressions. This would have to be done at an edition boundary. We would only +do promotion for aggregates, literals, and combinations thereof, and +`#[rustc_promotable]` would be removed from the standard library. From 1c6bc1bed859e8e4f63be1b6455ea959f085adab Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Mon, 4 May 2020 11:59:40 -0700 Subject: [PATCH 02/19] Typo --- text/0000-inline-const.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-inline-const.md b/text/0000-inline-const.md index b0f9bebfabf..3f48828cf81 100644 --- a/text/0000-inline-const.md +++ b/text/0000-inline-const.md @@ -75,7 +75,7 @@ fn foo() -> &u32 { However, the fact that we are executing the array initializer or expression after the `&` at compile-time is not obvious to the user. To avoid violating their assumptions, we are very careful to promote only in cases where the user -cannot possibly tell that their code is not executing at runtime. This means +cannot possibly tell that their code is not executing at runtime. This means a [long list of rules][prom-rules] for determining the promotability of expressions, and it means expressions that call a `const fn` or that result in a type with a `Drop` impl need to use a named `const` declaration. From 40bbfebcf8fa9ad35c052ed51f6582f91508ed30 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Mon, 4 May 2020 12:00:48 -0700 Subject: [PATCH 03/19] Add RFC PR number --- text/0000-inline-const.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-inline-const.md b/text/0000-inline-const.md index 3f48828cf81..99608052f35 100644 --- a/text/0000-inline-const.md +++ b/text/0000-inline-const.md @@ -1,6 +1,6 @@ - Feature Name: `inline_const` - Start Date: 2020-04-30 -- RFC PR: TBD +- RFC PR: [rust-lang/rfcs#2920](https://github.com/rust-lang/rfcs/pull/2920) - Rust Issue: TBD # Summary From d7f15624f9a4ebc6ce4e729fcfa262c8c600c246 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Mon, 4 May 2020 12:02:45 -0700 Subject: [PATCH 04/19] Remove `PathPattern` change --- text/0000-inline-const.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/text/0000-inline-const.md b/text/0000-inline-const.md index 99608052f35..041d3ea0040 100644 --- a/text/0000-inline-const.md +++ b/text/0000-inline-const.md @@ -182,11 +182,6 @@ This RFC extends the [grammar for patterns] to be, > | PathInExpression > | QualifiedPathInExpression > | ConstBlockExpression // new -> -> PathPattern : -> PathInExpression -> | QualifiedPathInExpression -> | ConstBlockExpression // new > ``` In both the expression and pattern context, an inline `const` behaves exactly From 275f29def496da627ef508467b0ce83d91a5aa2c Mon Sep 17 00:00:00 2001 From: ecstatic-morse Date: Mon, 4 May 2020 12:31:07 -0700 Subject: [PATCH 05/19] Fix typo Co-authored-by: Martin Carton --- text/0000-inline-const.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-inline-const.md b/text/0000-inline-const.md index 041d3ea0040..539c947ce0c 100644 --- a/text/0000-inline-const.md +++ b/text/0000-inline-const.md @@ -93,7 +93,7 @@ fn foo(x: &i32) -> &i32 { if *x < 0 { const { &4i32.pow(4) } } else { x } } -fn foo() -> &u32 {0..5 +fn foo() -> &u32 { let three_ranges = [const { (0..=5).into_inner() }; 3]; } ``` From 25963089258423baca3fe840279485519ca7f10c Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Mon, 4 May 2020 12:34:15 -0700 Subject: [PATCH 06/19] Add constants to list of things that should be promotable --- text/0000-inline-const.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-inline-const.md b/text/0000-inline-const.md index 539c947ce0c..874e56bc44f 100644 --- a/text/0000-inline-const.md +++ b/text/0000-inline-const.md @@ -272,5 +272,5 @@ variant of closures: `|| 42`. This is backwards compatible with the current prop This could allow us to deprecate the more esoteric classes of promotable expressions (e.g., `&(u32::MAX + u32::MAX)`) in favor of inline `const` expressions. This would have to be done at an edition boundary. We would only -do promotion for aggregates, literals, and combinations thereof, and +do promotion for aggregates, literals, constants and combinations thereof, and `#[rustc_promotable]` would be removed from the standard library. From 0e86426c9cca5c50d852951e0b5ee5fd4c545239 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Tue, 5 May 2020 10:43:00 -0700 Subject: [PATCH 07/19] Update naming discussion --- text/0000-inline-const.md | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/text/0000-inline-const.md b/text/0000-inline-const.md index 874e56bc44f..3f2ef609572 100644 --- a/text/0000-inline-const.md +++ b/text/0000-inline-const.md @@ -252,10 +252,19 @@ AFAIK, this was [first proposed] by @scottmcm. ## Naming I prefer the name inline `const`, since it signals that there is no difference -between a named `const` and an inline one. @scottmcm prefers "`const` block", -which is closer to the syntax and parallels the current terminology of `async` -block and `unsafe` block. It also avoids any accidental conflation with the -`#[inline]` attribute, which is unrelated. +between a named `const` and an inline one. + +@scottmcm prefers "`const` block", which is closer to the syntax and parallels +the current terminology of `async` block and `unsafe` block. It also avoids any +accidental conflation with the `#[inline]` attribute, which is unrelated. +Additionally, it doesn't extend nicely to the single-expression variant +discussed in [future possibilities]. + +@RalfJung prefers "anonymous `const`". @scottmcm mentioned in Zulip that this +could be confused with the `const _: () = ...;` syntax introduced in [RFC +2526]. The reference refers to these as "unnamed" constants. + +[RFC 2526]: https://github.com/rust-lang/rfcs/pull/2526 ## Lints @@ -263,7 +272,7 @@ As mentioned in the reference-level specification, we need to decide whether we want to lint against certain types of inline `const` expressions. # Future possibilities -[future-possibilities]: #future-possibilities +[future possibilities]: #future-possibilities It would be possible to allow the syntax `const expr` for an inline `const` that consists of a single expression. This is analagous to the single expression From b35e5595f225a89efdf3fbac58cb153b1f56dc29 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Tue, 5 May 2020 10:55:50 -0700 Subject: [PATCH 08/19] Expound on possibility of reducing the set of promotable expressions --- text/0000-inline-const.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/text/0000-inline-const.md b/text/0000-inline-const.md index 3f2ef609572..9e150e008e6 100644 --- a/text/0000-inline-const.md +++ b/text/0000-inline-const.md @@ -278,8 +278,14 @@ It would be possible to allow the syntax `const expr` for an inline `const` that consists of a single expression. This is analagous to the single expression variant of closures: `|| 42`. This is backwards compatible with the current proposal. -This could allow us to deprecate the more esoteric classes of promotable -expressions (e.g., `&(u32::MAX + u32::MAX)`) in favor of inline `const` -expressions. This would have to be done at an edition boundary. We would only -do promotion for aggregates, literals, constants and combinations thereof, and -`#[rustc_promotable]` would be removed from the standard library. +Eventually, I would like to try making any expression that could possibly panic +inelgible for implicit promotion. This includes *all* `const fn` calls as well +as all arithmetic expressions (e.g., `&(0u32 - 1)`), which currently work due +to [the way MIR is lowered][arith-assert]. Even though bitwise operators can +never panic, I would also stop promoting them to be consistent. This would +have to be done at an edition boundary. We would only do promotion for +aggregates, literals, constants and combinations thereof (**@RalfJung** notes +that this is the subset of the language valid in patterns), and +`#[rustc_promotable]` would be ignored by later editions of the compiler. + +[arith-assert]: https://github.com/rust-lang/const-eval/issues/19#issuecomment-447052258 From 6372ce932d36870cf0f24b3bf87ce053b0372026 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Tue, 5 May 2020 11:50:27 -0700 Subject: [PATCH 09/19] No version is idiomatic --- text/0000-inline-const.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/text/0000-inline-const.md b/text/0000-inline-const.md index 9e150e008e6..7b6840f4c4d 100644 --- a/text/0000-inline-const.md +++ b/text/0000-inline-const.md @@ -199,17 +199,21 @@ An inline `const` is eligible for promotion in an implicit context (just like a named `const`), so the following are all guaranteed to work: ```rust -let x: &'static i32 = &const { 4i32.pow(4) }; // NOT IDIOMATIC -let x: &'static i32 = const { &4i32.pow(4) }; // IDIOMATIC +let x: &'static i32 = &const { 4i32.pow(4) }; +let x: &'static i32 = const { &4i32.pow(4) }; // If RFC 2203 is stabilized -let v = [const { Vec::new() }; 3]; // IDIOMATIC -let v = const { [ Vec::new(); 3] }; // NOT IDIOMATIC +let v = [const { Vec::new() }; 3]; +let v = const { [Vec::new(); 3] }; ``` -Whether to lint against the non idiomatic versions is an open question. -Personally, I would like to lint against `&const {...}` but not `const { [expr; -3] }`. +I don't have strong feelings about which version should be preferred. +**@RalfJung** points out that `&const { 4 + 2 }` is more readable than `const { +&(4 + 2) }`. + +Note that it may be possible for RFC 2203 to use the explicit rules for +promotability when `T: !Copy`. In this case, the last part of the example above +could simply be written as `[Vec::new(); 3]`. Inline `const`s are allowed within `const` and `static` initializers, just as we currently allow nested `const` declarations. Whether to lint against inline From 20d7f70abf4064867654c44d1edcbc33c4fb6a2d Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Tue, 5 May 2020 11:56:15 -0700 Subject: [PATCH 10/19] Mention Zig `comptime` --- text/0000-inline-const.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/text/0000-inline-const.md b/text/0000-inline-const.md index 7b6840f4c4d..fc57bd9d50e 100644 --- a/text/0000-inline-const.md +++ b/text/0000-inline-const.md @@ -244,10 +244,14 @@ that they are best considered as a unit, however. # Prior art [prior-art]: #prior-art +Zig has the `comptime` keyword that [works similarly][zig] when it appears +before a block. + I'm not aware of equivalents in other languages. AFAIK, this was [first proposed] by @scottmcm. +[zig]: https://kristoff.it/blog/what-is-zig-comptime/#compile-time-function-calls [first proposed]: https://internals.rust-lang.org/t/quick-thought-const-blocks/7803/9 # Unresolved questions From dff0ecc918b825aacf978e034d36637f250a87da Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Tue, 5 May 2020 12:06:49 -0700 Subject: [PATCH 11/19] Add "Generic Parameters" section to unresolved questions --- text/0000-inline-const.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/text/0000-inline-const.md b/text/0000-inline-const.md index fc57bd9d50e..680f1354459 100644 --- a/text/0000-inline-const.md +++ b/text/0000-inline-const.md @@ -257,6 +257,27 @@ AFAIK, this was [first proposed] by @scottmcm. # Unresolved questions [unresolved-questions]: #unresolved-questions +## Generic Parameters + +Named constants defined inside a function cannot use generic parameters that +are in scope within that function. + +```rust +fn foo() { + const SIZE: usize = { std::mem::size_of::() }; // ERROR +} +``` + +If we use the same rule for inline constants, there would be a class of +implicitly promotable expressions (e.g. `std::ptr::null::()`, `T::CONST + +T::ANOTHER_CONST`), that could not be written inside an inline constant. We +would need to resolve this somehow before we stop promoting these expressions +as discussed in [future possibilites]. + +Alternatively, we could allow inline constants to refer to generic parameters. +Associated constants already behave this way, so I think this is possible. +However, it may result in more post-monomorphization const-eval errors. + ## Naming I prefer the name inline `const`, since it signals that there is no difference From ed83797051479925b064cb4209a8ad0530e3c597 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Tue, 5 May 2020 12:09:53 -0700 Subject: [PATCH 12/19] Link to const type inference RFC --- text/0000-inline-const.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/text/0000-inline-const.md b/text/0000-inline-const.md index 680f1354459..ea18dcaaabc 100644 --- a/text/0000-inline-const.md +++ b/text/0000-inline-const.md @@ -225,9 +225,11 @@ currently allow nested `const` declarations. Whether to lint against inline This excludes other uses of the `const` keyword in expressions and patterns. I'm not aware of any other proposals that would take advantage of this. -This would also be the first use of type inference for const initializers. I'm -not aware of any technical issues that would arise from this, but perhaps I'm -overlooking something? +This would also be the first use of type inference for const initializers. Type +inference for named constants was proposed in [RFC 1349]. I don't believe the +blockers for this were technical, so I think this is possible. + +[RFC 1349]: https://github.com/rust-lang/rfcs/issues/1349 # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives From c89075d26348c179989b69188619b8e05d34c546 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Tue, 5 May 2020 12:11:38 -0700 Subject: [PATCH 13/19] Bold usernames --- text/0000-inline-const.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/text/0000-inline-const.md b/text/0000-inline-const.md index ea18dcaaabc..04bfc7bad87 100644 --- a/text/0000-inline-const.md +++ b/text/0000-inline-const.md @@ -251,7 +251,7 @@ before a block. I'm not aware of equivalents in other languages. -AFAIK, this was [first proposed] by @scottmcm. +AFAIK, this was [first proposed] by **@scottmcm**. [zig]: https://kristoff.it/blog/what-is-zig-comptime/#compile-time-function-calls [first proposed]: https://internals.rust-lang.org/t/quick-thought-const-blocks/7803/9 @@ -285,15 +285,15 @@ However, it may result in more post-monomorphization const-eval errors. I prefer the name inline `const`, since it signals that there is no difference between a named `const` and an inline one. -@scottmcm prefers "`const` block", which is closer to the syntax and parallels +**@scottmcm** prefers "`const` block", which is closer to the syntax and parallels the current terminology of `async` block and `unsafe` block. It also avoids any accidental conflation with the `#[inline]` attribute, which is unrelated. Additionally, it doesn't extend nicely to the single-expression variant discussed in [future possibilities]. -@RalfJung prefers "anonymous `const`". @scottmcm mentioned in Zulip that this -could be confused with the `const _: () = ...;` syntax introduced in [RFC -2526]. The reference refers to these as "unnamed" constants. +**@RalfJung** prefers "anonymous `const`". **@scottmcm** mentioned in Zulip +that this could be confused with the `const _: () = ...;` syntax introduced in +[RFC 2526]. The reference refers to these as "unnamed" constants. [RFC 2526]: https://github.com/rust-lang/rfcs/pull/2526 From c233d4de2d90a91354176e6b2caf19cdd1c4eb6a Mon Sep 17 00:00:00 2001 From: ecstatic-morse Date: Sun, 10 May 2020 12:23:01 -0700 Subject: [PATCH 14/19] Fix typo Co-authored-by: Jake Goulding --- text/0000-inline-const.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-inline-const.md b/text/0000-inline-const.md index 04bfc7bad87..62d459c7413 100644 --- a/text/0000-inline-const.md +++ b/text/0000-inline-const.md @@ -310,7 +310,7 @@ consists of a single expression. This is analagous to the single expression variant of closures: `|| 42`. This is backwards compatible with the current proposal. Eventually, I would like to try making any expression that could possibly panic -inelgible for implicit promotion. This includes *all* `const fn` calls as well +ineligible for implicit promotion. This includes *all* `const fn` calls as well as all arithmetic expressions (e.g., `&(0u32 - 1)`), which currently work due to [the way MIR is lowered][arith-assert]. Even though bitwise operators can never panic, I would also stop promoting them to be consistent. This would From e67c40bb165dc3bd24859ef5785102dee30b6005 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Tue, 2 Jun 2020 09:00:30 -0700 Subject: [PATCH 15/19] Better const pattern example --- text/0000-inline-const.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/text/0000-inline-const.md b/text/0000-inline-const.md index 62d459c7413..75cd3fd3763 100644 --- a/text/0000-inline-const.md +++ b/text/0000-inline-const.md @@ -22,10 +22,16 @@ fn mock_ip(use_localhost: bool) -> &'static Ipv6Addr { } } +const MMIO_BIT1: u8 = 4; +const MMIO_BIT2: u8 = 5; + fn main() { - match *x { - 0 ..= const { u32::MAX / 2 } => println!("low"), - const { u32::MAX / 2 + 1 } ..= u32::MAX => println!("high"), + match read_mmio() { + 0 => {} + const { 1 << MMIO_BIT1 } => println!("FOO"), + const { 1 << MMIO_BIT2 } => println!("BAR"), + + _ => unreachable!(), } } ``` From 798b26a6b75e2113463ca36901b31db8a92b0e13 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Tue, 2 Jun 2020 09:28:25 -0700 Subject: [PATCH 16/19] Update resolved/unresolved questions in RFC - "Generic parameters" are resolved to work just like array length expressions. - Added section about containing `unsafe` blocks. - Moved "lints" to unresolved questions. --- text/0000-inline-const.md | 128 ++++++++++++++++++++++---------------- 1 file changed, 75 insertions(+), 53 deletions(-) diff --git a/text/0000-inline-const.md b/text/0000-inline-const.md index 75cd3fd3763..4a6bfa41c17 100644 --- a/text/0000-inline-const.md +++ b/text/0000-inline-const.md @@ -91,8 +91,19 @@ impl need to use a named `const` declaration. # Guide-level explanation [guide-level-explanation]: #guide-level-explanation -This proposal is a middle ground, which is less verbose than named constants but -more obvious and expressive than promotion. +This proposal is a middle ground, which is less verbose than named constants +but more obvious and expressive than promotion. In expression context, it +behaves much like the user had written the following, where `Ty` is the +inferred type of the code within the inline `const` expression (represented by +the ellipsis): + +```rust +{ const UNIQUE_IDENT: Ty = ...; UNIQUE_IDENT } +``` + +With this extension to the language, users can ensure that their code executes +at compile-time without needing to declare a separate `const` item that is only +used once. ```rust fn foo(x: &i32) -> &i32 { @@ -104,10 +115,6 @@ fn foo() -> &u32 { } ``` -With this extension to the language, users can ensure that their code executes -at compile-time without needing to declare a separate `const` item that is only -used once. - ## Patterns Patterns are another context that require a named `const` when using complex @@ -190,40 +197,55 @@ This RFC extends the [grammar for patterns] to be, > | ConstBlockExpression // new > ``` -In both the expression and pattern context, an inline `const` behaves exactly -as if the user had declared a uniquely identified `const` with the block's -contents as its initializer. For example, in expression context, writing -`const { ... }` is equivalent to writing: +In both the expression and pattern context, an inline `const` behaves as if the +user had declared a uniquely named constant in the containing scope and +referenced it. + +## Generic Parameters + +For now, inline `const` expressions and patterns cannot refer to in-scope +generic parameters. As of this writing, the same restriction applies to array +length expressions, which seem like a good precedent for this RFC. As far as I +know, this is only a temporary restriction; the long-term goal is to allow +array length expressions to use generic parameters. When this happens, inline +`const` expressions and patterns will also be allowed to refer to in-scope +generics. ```rust -{ const UNIQUE_IDENT: Ty = ...; UNIQUE_IDENT } +fn foo() { + let x = [4i32; std::mem::size_of::()]; // NOT ALLOWED (for now) + let x = const { std::mem::size_of::() }; // NOT ALLOWED (for now) +} ``` -where `Ty` is inferred from the expression inside the braces. +## Containing `unsafe` -An inline `const` is eligible for promotion in an implicit context (just like a -named `const`), so the following are all guaranteed to work: +At present, containing `unsafe` blocks do not apply to array length expressions inside: ```rust -let x: &'static i32 = &const { 4i32.pow(4) }; -let x: &'static i32 = const { &4i32.pow(4) }; - -// If RFC 2203 is stabilized -let v = [const { Vec::new() }; 3]; -let v = const { [Vec::new(); 3] }; +fn bar() { + let x = unsafe { + [4i32; std::intrinsics::unchecked_add(2i32, 3i32)] // ERROR + }; +} ``` -I don't have strong feelings about which version should be preferred. -**@RalfJung** points out that `&const { 4 + 2 }` is more readable than `const { -&(4 + 2) }`. +I find this somewhat strange, but consistency is important, so inline `const` +expressions should behave the same way. The following would also fail to +compile: -Note that it may be possible for RFC 2203 to use the explicit rules for -promotability when `T: !Copy`. In this case, the last part of the example above -could simply be written as `[Vec::new(); 3]`. +```rust +fn bar() { + let x = unsafe { + const { std::intrinsics::unchecked_add(2i32, 3i32) } // ERROR + }; +} +``` -Inline `const`s are allowed within `const` and `static` initializers, just as we -currently allow nested `const` declarations. Whether to lint against inline -`const` expressions inside a `const` or `static` is also an open question. +If [#72359] is considered a bug and resolved, that change would also apply to +inline `const` expressions and patterns. + +[#72359]: https://github.com/rust-lang/rust/issues/72359 # Drawbacks [drawbacks]: #drawbacks @@ -265,27 +287,6 @@ AFAIK, this was [first proposed] by **@scottmcm**. # Unresolved questions [unresolved-questions]: #unresolved-questions -## Generic Parameters - -Named constants defined inside a function cannot use generic parameters that -are in scope within that function. - -```rust -fn foo() { - const SIZE: usize = { std::mem::size_of::() }; // ERROR -} -``` - -If we use the same rule for inline constants, there would be a class of -implicitly promotable expressions (e.g. `std::ptr::null::()`, `T::CONST + -T::ANOTHER_CONST`), that could not be written inside an inline constant. We -would need to resolve this somehow before we stop promoting these expressions -as discussed in [future possibilites]. - -Alternatively, we could allow inline constants to refer to generic parameters. -Associated constants already behave this way, so I think this is possible. -However, it may result in more post-monomorphization const-eval errors. - ## Naming I prefer the name inline `const`, since it signals that there is no difference @@ -303,10 +304,31 @@ that this could be confused with the `const _: () = ...;` syntax introduced in [RFC 2526]: https://github.com/rust-lang/rfcs/pull/2526 -## Lints +## Lints about placement of inline `const` + +An inline `const` is eligible for promotion in an implicit context (just like a +named `const`), so the following are all guaranteed to work: + +```rust +let x: &'static i32 = &const { 4i32.pow(4) }; +let x: &'static i32 = const { &4i32.pow(4) }; + +// If RFC 2203 is stabilized +let v = [const { Vec::new() }; 3]; +let v = const { [Vec::new(); 3] }; +``` + +I don't have strong feelings about which version should be preferred. +**@RalfJung** points out that `&const { 4 + 2 }` is more readable than `const { +&(4 + 2) }`. + +Note that it may be possible for RFC 2203 to use the explicit rules for +promotability when `T: !Copy`. In this case, the last part of the example above +could simply be written as `[Vec::new(); 3]`. -As mentioned in the reference-level specification, we need to decide whether we -want to lint against certain types of inline `const` expressions. +Inline `const`s are allowed within `const` and `static` initializers, just as we +currently allow nested `const` declarations. Whether to lint against inline +`const` expressions inside a `const` or `static` is also an open question. # Future possibilities [future possibilities]: #future-possibilities From 1f9d6976fe7334e572d02006fb457f63d1db352e Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Wed, 3 Jun 2020 10:19:16 -0700 Subject: [PATCH 17/19] Remove value judgement on separability --- text/0000-inline-const.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/text/0000-inline-const.md b/text/0000-inline-const.md index 4a6bfa41c17..17717ea0315 100644 --- a/text/0000-inline-const.md +++ b/text/0000-inline-const.md @@ -268,8 +268,7 @@ approach](https://github.com/rust-lang/rust/pull/70042#issuecomment-612221597) instead. It would also possible to separate out the parts of this RFC relating to patterns -so that they can be decided upon seperately. I think they are similar enough -that they are best considered as a unit, however. +so that they can be decided upon seperately. # Prior art [prior-art]: #prior-art From e679523b6863aa48700165645ef177508e5437ee Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Wed, 3 Jun 2020 10:22:54 -0700 Subject: [PATCH 18/19] Make promotion section more hypothetical --- text/0000-inline-const.md | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/text/0000-inline-const.md b/text/0000-inline-const.md index 17717ea0315..cca75188d50 100644 --- a/text/0000-inline-const.md +++ b/text/0000-inline-const.md @@ -336,14 +336,7 @@ It would be possible to allow the syntax `const expr` for an inline `const` that consists of a single expression. This is analagous to the single expression variant of closures: `|| 42`. This is backwards compatible with the current proposal. -Eventually, I would like to try making any expression that could possibly panic -ineligible for implicit promotion. This includes *all* `const fn` calls as well -as all arithmetic expressions (e.g., `&(0u32 - 1)`), which currently work due -to [the way MIR is lowered][arith-assert]. Even though bitwise operators can -never panic, I would also stop promoting them to be consistent. This would -have to be done at an edition boundary. We would only do promotion for -aggregates, literals, constants and combinations thereof (**@RalfJung** notes -that this is the subset of the language valid in patterns), and -`#[rustc_promotable]` would be ignored by later editions of the compiler. - -[arith-assert]: https://github.com/rust-lang/const-eval/issues/19#issuecomment-447052258 +At some point (an edition boundary?), we may want to narrow the scope of +expressions that are eligible for implicit promotion. Inline `const` +expressions would be the recommended replacement for expressions that were no +longer eligible. From ee9164a2f6cccc14caef7c36b12a52d86bb00f35 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Wed, 3 Jun 2020 12:11:21 -0700 Subject: [PATCH 19/19] Fix typos --- text/0000-inline-const.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/text/0000-inline-const.md b/text/0000-inline-const.md index cca75188d50..70ee264f548 100644 --- a/text/0000-inline-const.md +++ b/text/0000-inline-const.md @@ -60,7 +60,7 @@ fn foo() -> &u32 { } ``` -Writing out a `const` declaration everytime we need a long-lived reference or +Writing out a `const` declaration every time we need a long-lived reference or a non-`Copy` array initializer can be annoying. To improve the situation, [RFC 1414] introduced rvalue static promotion to extend lifetimes, and [RFC 2203] extended the concept of promotion to array initializers. @@ -268,7 +268,7 @@ approach](https://github.com/rust-lang/rust/pull/70042#issuecomment-612221597) instead. It would also possible to separate out the parts of this RFC relating to patterns -so that they can be decided upon seperately. +so that they can be decided upon separately. # Prior art [prior-art]: #prior-art @@ -333,7 +333,7 @@ currently allow nested `const` declarations. Whether to lint against inline [future possibilities]: #future-possibilities It would be possible to allow the syntax `const expr` for an inline `const` that -consists of a single expression. This is analagous to the single expression +consists of a single expression. This is analogous to the single expression variant of closures: `|| 42`. This is backwards compatible with the current proposal. At some point (an edition boundary?), we may want to narrow the scope of