Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 43 additions & 10 deletions src/destructors.md
Original file line number Diff line number Diff line change
Expand Up @@ -398,11 +398,7 @@ println!("{:?}", C);
```

r[destructors.scope.lifetime-extension.sub-expressions]
If a [borrow][borrow expression], [dereference][dereference expression],
[field][field expression], or [tuple indexing expression] has an extended
temporary scope then so does its operand. If an [indexing expression] has an
extended temporary scope then the indexed expression also has an extended
temporary scope.
If a [borrow], [dereference][dereference expression], [field][field expression], or [tuple indexing expression] has an extended temporary scope then so does its operand. If an [indexing expression] has an extended temporary scope then the indexed expression also has an extended temporary scope.

r[destructors.scope.lifetime-extension.patterns]
#### Extending based on patterns
Expand Down Expand Up @@ -474,11 +470,13 @@ let &ref x = &*&temp(); // OK
r[destructors.scope.lifetime-extension.exprs]
#### Extending based on expressions

r[destructors.scope.lifetime-extension.exprs.extending]
For a let statement with an initializer, an *extending expression* is an
expression which is one of the following:

* The initializer expression.
* The operand of an extending [borrow expression].
* The operand of an extending [borrow] expression.
* The [super operands] of an extending [super macro call] expression.
* The operand(s) of an extending [array][array expression], [cast][cast
expression], [braced struct][struct expression], or [tuple][tuple expression]
expression.
Expand All @@ -490,8 +488,11 @@ expression which is one of the following:
So the borrow expressions in `&mut 0`, `(&1, &mut 2)`, and `Some(&mut 3)`
are all extending expressions. The borrows in `&0 + &1` and `f(&mut 0)` are not.

The operand of any extending borrow expression has its temporary scope
extended.
r[destructors.scope.lifetime-extension.exprs.borrows]
The operand of an extending [borrow] expression has its [temporary scope] [extended].

r[destructors.scope.lifetime-extension.exprs.super-macros]
The [super temporaries] of an extending [super macro call] expression have their [scopes][temporary scopes] [extended].

> [!NOTE]
> `rustc` does not treat [array repeat operands] of extending [array] expressions as extending expressions. Whether it should is an open question.
Expand All @@ -503,9 +504,10 @@ extended.
Here are some examples where expressions have extended temporary scopes:

```rust,edition2024
# use core::pin::pin;
# use core::sync::atomic::{AtomicU64, Ordering::Relaxed};
# static X: AtomicU64 = AtomicU64::new(0);
# struct S;
# #[derive(Debug)] struct S;
# impl Drop for S { fn drop(&mut self) { X.fetch_add(1, Relaxed); } }
# const fn temp() -> S { S }
let x = &temp(); // Operand of borrow.
Expand All @@ -528,6 +530,14 @@ let x = if true { &temp() } else { &temp() };
# x;
let x = match () { _ => &temp() }; // `match` arm expression.
# x;
let x = pin!(temp()); // Super operand of super macro call expression.
# x;
let x = pin!({ &mut temp() }); // As above.
# x;
# // FIXME: Simplify after this PR lands:
# // <https://github.com/rust-lang/rust/pull/145882>.
let x = format_args!("{:?}{:?}", (), temp()); // As above.
# x;
//
// All of the temporaries above are still live here.
# assert_eq!(0, X.load(Relaxed));
Expand Down Expand Up @@ -587,6 +597,23 @@ let x = 'a: { break 'a &temp() }; // ERROR
# x;
```

```rust,edition2024,compile_fail,E0716
# use core::pin::pin;
# fn temp() {}
// The argument to `pin!` is only an extending expression if the call
// is an extending expression. Since it's not, the inner block is not
// an extending expression, so the temporaries in its trailing
// expression are dropped immediately.
pin!({ &temp() }); // ERROR
```

<!-- FIXME: Simplify after https://github.com/rust-lang/rust/pull/145882 lands. -->
```rust,edition2024,compile_fail,E0716
# fn temp() {}
// As above.
format_args!("{:?}{:?}", (), { &temp() }); // ERROR
```

r[destructors.forget]
## Not running destructors

Expand Down Expand Up @@ -647,12 +674,18 @@ There is one additional case to be aware of: when a panic reaches a [non-unwindi
[array repeat operands]: expr.array.repeat-operand
[async block expression]: expr.block.async
[block expression]: expressions/block-expr.md
[borrow expression]: expressions/operator-expr.md#borrow-operators
[borrow]: expr.operator.borrow
[cast expression]: expressions/operator-expr.md#type-cast-expressions
[dereference expression]: expressions/operator-expr.md#the-dereference-operator
[extended]: destructors.scope.lifetime-extension
[field expression]: expressions/field-expr.md
[indexing expression]: expressions/array-expr.md#array-and-slice-indexing-expressions
[struct expression]: expressions/struct-expr.md
[super macro call]: expr.super-macros
[super operands]: expr.super-macros
[super temporaries]: expr.super-macros
[temporary scope]: destructors.scope.temporary
[temporary scopes]: destructors.scope.temporary
[tuple expression]: expressions/tuple-expr.md#tuple-expressions
[tuple indexing expression]: expressions/tuple-expr.md#tuple-indexing-expressions

Expand Down
99 changes: 99 additions & 0 deletions src/expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,92 @@ When using a value expression in most place expression contexts, a temporary unn
The expression evaluates to that location instead, except if [promoted] to a `static`.
The [drop scope] of the temporary is usually the end of the enclosing statement.

r[expr.super-macros]
### Super macros

r[expr.super-macros.intro]
Certain built-in macros may create [temporaries] whose [scopes][temporary scopes] may be [extended]. These temporaries are *super temporaries* and these macros are *super macros*. [Invocations][macro invocations] of these macros are *super macro call expressions*. Arguments to these macros may be *super operands*.

> [!NOTE]
> When a super macro call expression is an [extending expression], its super operands are [extending expressions] and the [scopes][temporary scopes] of the super temporaries are [extended]. See [destructors.scope.lifetime-extension.exprs].

r[expr.super-macros.format_args]
#### `format_args!`

r[expr.super-macros.format_args.super-operands]
Except for the format string argument, all arguments passed to [`format_args!`] are *super operands*.

<!-- FIXME: Remove after https://github.com/rust-lang/rust/pull/145882 lands. -->
> [!NOTE]
> When there is only one placeholder, `rustc` does not yet treat the corresponding argument as a super operand. This is a bug.
>
> For details, see Rust issue [#145880](https://github.com/rust-lang/rust/issues/145880).

<!-- FIXME: Simplify after https://github.com/rust-lang/rust/pull/145882 lands. -->
```rust,edition2024
# fn temp() -> String { String::from("") }
// Due to the call being an extending expression and the argument
// being a super operand, the inner block is an extending expression,
// so the scope of the temporary created in its trailing expression
// is extended.
let _ = format_args!("{:?}{}", (), { &temp() }); // OK
```

r[expr.super-macros.format_args.super-temporaries]
The super operands of [`format_args!`] are [implicitly borrowed] and are therefore [place expression contexts]. When a [value expression] is passed as an argument, it creates a *super temporary*.

<!-- FIXME: Simplify after https://github.com/rust-lang/rust/pull/145882 lands. -->
```rust
# fn temp() -> String { String::from("") }
let x = format_args!("{}{}", temp(), temp());
x; // <-- The temporaries are extended, allowing use here.
```

The expansion of a call to [`format_args!`] sometimes creates other internal *super temporaries*.

```rust,compile_fail,E0716
let x = {
// This call creates an internal temporary.
let x = format_args!("{:?}", 0);
x // <-- The temporary is extended, allowing its use here.
}; // <-- The temporary is dropped here.
x; // ERROR
```

```rust
// This call doesn't create an internal temporary.
let x = { let x = format_args!("{}", 0); x };
x; // OK
```

> [!NOTE]
> The details of when [`format_args!`] does or does not create internal temporaries are currently unspecified.

r[expr.super-macros.pin]
#### `pin!`

r[expr.super-macros.pin.super-operands]
The argument to [`pin!`] is a *super operand*.

```rust,edition2024
# use core::pin::pin;
# fn temp() {}
// As above for `format_args!`.
let _ = pin!({ &temp() }); // OK
```

r[expr.super-macros.pin.super-temporaries]
The argument to [`pin!`] is a [value expression context] and creates a *super temporary*.

```rust
# use core::pin::pin;
# fn temp() {}
// The argument is evaluated into a super temporary.
let x = pin!(temp());
// The temporary is extended, allowing its use here.
x; // OK
```

r[expr.implicit-borrow]
### Implicit Borrows

Expand Down Expand Up @@ -285,6 +371,7 @@ Implicit borrows may be taken in the following expressions:
* Operand of the [dereference operator][deref] (`*`).
* Operands of [comparison].
* Left operands of the [compound assignment].
* Arguments to [`format_args!`] except the format string.

r[expr.overload]
## Overloading Traits
Expand All @@ -311,6 +398,8 @@ They are never allowed before:
[`Copy`]: special-types-and-traits.md#copy
[`Drop`]: special-types-and-traits.md#drop
[`if let`]: expressions/if-expr.md#if-let-patterns
[`format_args!`]: core::format_args
[`pin!`]: core::pin::pin
[`Sized`]: special-types-and-traits.md#sized
[`while let`]: expressions/loop-expr.md#while-let-patterns
[array expressions]: expressions/array-expr.md
Expand All @@ -324,17 +413,23 @@ They are never allowed before:
[deref]: expressions/operator-expr.md#the-dereference-operator
[destructors]: destructors.md
[drop scope]: destructors.md#drop-scopes
[extended]: destructors.scope.lifetime-extension
[extending expression]: destructors.scope.lifetime-extension.exprs
[extending expressions]: destructors.scope.lifetime-extension.exprs
[field]: expressions/field-expr.md
[functional update]: expressions/struct-expr.md#functional-update-syntax
[implicit borrow]: #implicit-borrows
[implicitly borrowed]: expr.implicit-borrow
[implicitly mutably borrowed]: #implicit-borrows
[interior mutability]: interior-mutability.md
[let statement]: statements.md#let-statements
[macro invocations]: macro.invocation
[match]: expressions/match-expr.md
[method-call]: expressions/method-call-expr.md
[Mutable `static` items]: items/static-items.md#mutable-statics
[Outer attributes]: attributes.md
[paths]: expressions/path-expr.md
[place expression contexts]: expr.place-value
[promoted]: destructors.md#constant-promotion
[Range]: expressions/range-expr.md
[raw borrow]: expressions/operator-expr.md#raw-borrow-operators
Expand All @@ -344,10 +439,14 @@ They are never allowed before:
[static variables]: items/static-items.md
[struct]: expressions/struct-expr.md
[Structs]: expr.struct
[temporaries]: expr.temporary
[temporary scopes]: destructors.scope.temporary
[Temporary values]: #temporaries
[tuple expressions]: expressions/tuple-expr.md
[Tuple structs]: items.struct.tuple
[Tuples]: expressions/tuple-expr.md
[Underscores]: expressions/underscore-expr.md
[Unit structs]: items.struct.unit
[value expression context]: expr.place-value
[value expression]: expr.place-value
[Variables]: variables.md