Rollup merge of #139080 - m-ou-se:super-let-gate, r=traviscross

Experimental feature gate for `super let`

This adds an experimental feature gate, `#![feature(super_let)]`, for the `super let` experiment.

Tracking issue: https://github.com/rust-lang/rust/issues/139076

Liaison: ``@nikomatsakis``

## Description

There's a rough (inaccurate) description here: https://blog.m-ou.se/super-let/

In short, `super let` allows you to define something that lives long enough to be borrowed by the tail expression of the block. For example:

```rust
let a = {
    super let b = temp();
    &b
};
```

Here, `b` is extended to live as long as `a`, similar to how in `let a = &temp();`, the temporary will be extended to live as long as `a`.

## Properties

During the temporary lifetimes work we did last year, we explored the properties of "super let" and concluded that the fundamental property should be that these two are always equivalent in any context:

1. `& $expr`
2. `{ super let a = & $expr; a }`

And, additionally, that these are equivalent in any context when `$expr` is a temporary (aka rvalue):

1. `& $expr`
2. `{ super let a = $expr; & a }`

This makes it possible to give a name to a temporary without affecting how temporary lifetimes work, such that a macro can transparently use a block in its expansion, without that having any effect on the outside.

## Implementing pin!() correctly

With `super let`, we can properly implement the `pin!()` macro without hacks: 

```rust
pub macro pin($value:expr $(,)?) {
    {
        super let mut pinned = $value;
        unsafe { $crate::pin::Pin::new_unchecked(&mut pinned) }
    }
}
```

This is important, as there is currently no way to express it without hacks in Rust 2021 and before (see [hacky definition](2a06022951/library/core/src/pin.rs (L1947))), and no way to express it at all in Rust 2024 (see [issue](https://github.com/rust-lang/rust/issues/138718)).

## Fixing format_args!()

This will also allow us to express `format_args!()` in a way where one can assign the result to a variable, fixing a [long standing issue](https://github.com/rust-lang/rust/issues/92698):

```rust
let f = format_args!("Hello {name}!"); // error today, but accepted in the future! (after separate FCP)
```

## Experiment

The precise definition of `super let`, what happens for `super let x;` (without initializer), and whether to accept `super let _ = _ else { .. }` are still open questions, to be answered by the experiment.

Furthermore, once we have a more complete understanding of the feature, we might be able to come up with a better syntax. (Which could be just a different keywords, or an entirely different way of naming temporaries that doesn't involve a block and a (super) let statement.)
This commit is contained in:
Matthias Krüger 2025-04-03 07:39:05 +02:00 committed by GitHub
commit dbd7f52c83
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 39 additions and 1 deletions

View File

@ -511,6 +511,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
gate_all!(contracts, "contracts are incomplete");
gate_all!(contracts_internals, "contract internal machinery is for internal use only");
gate_all!(where_clause_attrs, "attributes in `where` clause are unstable");
gate_all!(super_let, "`super let` is experimental");
if !visitor.features.never_patterns() {
if let Some(spans) = spans.get(&sym::never_patterns) {

View File

@ -629,6 +629,8 @@ declare_features! (
(unstable, strict_provenance_lints, "1.61.0", Some(130351)),
/// Allows string patterns to dereference values to match them.
(unstable, string_deref_patterns, "1.67.0", Some(87121)),
/// Allows `super let` statements.
(incomplete, super_let, "CURRENT_RUSTC_VERSION", Some(139076)),
/// Allows subtrait items to shadow supertrait items.
(unstable, supertrait_item_shadowing, "1.86.0", Some(89151)),
/// Allows using `#[thread_local]` on `static` items.

View File

@ -73,7 +73,20 @@ impl<'a> Parser<'a> {
});
}
let stmt = if self.token.is_keyword(kw::Let) {
let stmt = if self.token.is_keyword(kw::Super) && self.is_keyword_ahead(1, &[kw::Let]) {
self.collect_tokens(None, attrs, force_collect, |this, attrs| {
this.expect_keyword(exp!(Super))?;
this.psess.gated_spans.gate(sym::super_let, this.prev_token.span);
this.expect_keyword(exp!(Let))?;
let local = this.parse_local(attrs)?; // FIXME(mara): implement super let
let trailing = Trailing::from(capture_semi && this.token == token::Semi);
Ok((
this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)),
trailing,
UsePreAttrPos::No,
))
})?
} else if self.token.is_keyword(kw::Let) {
self.collect_tokens(None, attrs, force_collect, |this, attrs| {
this.expect_keyword(exp!(Let))?;
let local = this.parse_local(attrs)?;

View File

@ -114,6 +114,7 @@ pub enum TokenType {
KwSelfUpper,
KwStatic,
KwStruct,
KwSuper,
KwTrait,
KwTry,
KwType,
@ -250,6 +251,7 @@ impl TokenType {
KwSelfUpper,
KwStatic,
KwStruct,
KwSuper,
KwTrait,
KwTry,
KwType,
@ -324,6 +326,7 @@ impl TokenType {
TokenType::KwSelfUpper => Some(kw::SelfUpper),
TokenType::KwStatic => Some(kw::Static),
TokenType::KwStruct => Some(kw::Struct),
TokenType::KwSuper => Some(kw::Super),
TokenType::KwTrait => Some(kw::Trait),
TokenType::KwTry => Some(kw::Try),
TokenType::KwType => Some(kw::Type),
@ -549,6 +552,7 @@ macro_rules! exp {
(SelfUpper) => { exp!(@kw, SelfUpper, KwSelfUpper) };
(Static) => { exp!(@kw, Static, KwStatic) };
(Struct) => { exp!(@kw, Struct, KwStruct) };
(Super) => { exp!(@kw, Super, KwSuper) };
(Trait) => { exp!(@kw, Trait, KwTrait) };
(Try) => { exp!(@kw, Try, KwTry) };
(Type) => { exp!(@kw, Type, KwType) };

View File

@ -2040,6 +2040,7 @@ symbols! {
sub_assign,
sub_with_overflow,
suggestion,
super_let,
supertrait_item_shadowing,
surface_async_drop_in_place,
sym,

View File

@ -0,0 +1,4 @@
fn main() {
super let a = 1;
//~^ ERROR `super let` is experimental
}

View File

@ -0,0 +1,13 @@
error[E0658]: `super let` is experimental
--> $DIR/feature-gate-super-let.rs:2:5
|
LL | super let a = 1;
| ^^^^^
|
= note: see issue #139076 <https://github.com/rust-lang/rust/issues/139076> for more information
= help: add `#![feature(super_let)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0658`.