Skip to content

Commit 29cafd4

Browse files
committed
Revise text on built-in macro lifetime extension
Rather than discussing the built-in macros directly in the context of extending expressions, let's define "super macros", "super operands", and "super temporaries". It's unfortunate to have to introduce so many terms, but it still seems a bit clearer as the terms help to disentangle the many different things at play.
1 parent 8b24e65 commit 29cafd4

File tree

2 files changed

+138
-21
lines changed

2 files changed

+138
-21
lines changed

src/destructors.md

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -398,11 +398,7 @@ println!("{:?}", C);
398398
```
399399

400400
r[destructors.scope.lifetime-extension.sub-expressions]
401-
If a [borrow][borrow expression], [dereference][dereference expression],
402-
[field][field expression], or [tuple indexing expression] has an extended
403-
temporary scope then so does its operand. If an [indexing expression] has an
404-
extended temporary scope then the indexed expression also has an extended
405-
temporary scope.
401+
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.
406402

407403
r[destructors.scope.lifetime-extension.patterns]
408404
#### Extending based on patterns
@@ -479,26 +475,24 @@ For a let statement with an initializer, an *extending expression* is an
479475
expression which is one of the following:
480476

481477
* The initializer expression.
482-
* The operand of an extending [borrow expression].
478+
* The operand of an extending [borrow] expression.
479+
* The [super operands] of an extending [super macro call] expression.
483480
* The operand(s) of an extending [array][array expression], [cast][cast
484481
expression], [braced struct][struct expression], or [tuple][tuple expression]
485482
expression.
486483
* The arguments to an extending [tuple struct] or [tuple variant] constructor expression.
487484
* The final expression of an extending [block expression] except for an [async block expression].
488485
* The final expression of an extending [`if`] expression's consequent, `else if`, or `else` block.
489486
* An arm expression of an extending [`match`] expression.
490-
* The argument(s) to an extending [`pin!`] or [`format_args!`] [macro invocation] expression.
491487

492488
So the borrow expressions in `&mut 0`, `(&1, &mut 2)`, and `Some(&mut 3)`
493489
are all extending expressions. The borrows in `&0 + &1` and `f(&mut 0)` are not.
494490

495-
r[destructors.scope.lifetime-extension.exprs.borrow]
496-
The operand of any extending borrow expression has its temporary scope
497-
extended.
491+
r[destructors.scope.lifetime-extension.exprs.borrows]
492+
The operand of an extending [borrow] expression has its [temporary scope] [extended].
498493

499-
r[destructors.scope.lifetime-extension.exprs.macros]
500-
The built-in macros [`pin!`] and [`format_args!`] create temporaries.
501-
Any extending [`pin!`] or [`format_args!`] [macro invocation] expression has an extended temporary scope.
494+
r[destructors.scope.lifetime-extension.exprs.super-macros]
495+
The [super temporaries] of an extending [super macro call] expression have their [scopes][temporary scopes] [extended].
502496

503497
> [!NOTE]
504498
> `rustc` does not treat [array repeat operands] of extending [array] expressions as extending expressions. Whether it should is an open question.
@@ -510,10 +504,10 @@ Any extending [`pin!`] or [`format_args!`] [macro invocation] expression has an
510504
Here are some examples where expressions have extended temporary scopes:
511505

512506
```rust,edition2024
507+
# use core::pin::pin;
513508
# use core::sync::atomic::{AtomicU64, Ordering::Relaxed};
514-
# use std::pin::pin;
515509
# static X: AtomicU64 = AtomicU64::new(0);
516-
# struct S;
510+
# #[derive(Debug)] struct S;
517511
# impl Drop for S { fn drop(&mut self) { X.fetch_add(1, Relaxed); } }
518512
# const fn temp() -> S { S }
519513
let x = &temp(); // Operand of borrow.
@@ -536,7 +530,13 @@ let x = if true { &temp() } else { &temp() };
536530
# x;
537531
let x = match () { _ => &temp() }; // `match` arm expression.
538532
# x;
539-
let x = pin!(&temp()); // Argument to `pin!`.
533+
let x = pin!(temp()); // Super operand of super macro call expression.
534+
# x;
535+
let x = pin!({ &mut temp() }); // As above.
536+
# x;
537+
# // TODO: Simplify after this PR lands:
538+
# // <https://github.com/rust-lang/rust/pull/145882>.
539+
let x = format_args!("{:?}{:?}", (), temp()); // As above.
540540
# x;
541541
//
542542
// All of the temporaries above are still live here.
@@ -597,6 +597,23 @@ let x = 'a: { break 'a &temp() }; // ERROR
597597
# x;
598598
```
599599

600+
```rust,edition2024,compile_fail,E0716
601+
# use core::pin::pin;
602+
# fn temp() {}
603+
// The argument to `pin!` is only an extending expression if the call
604+
// is an extending expression. Since it's not, the inner block is not
605+
// an extending expression, so the temporaries in its trailing
606+
// expression are dropped immediately.
607+
pin!({ &temp() }); // ERROR
608+
```
609+
610+
<!-- TODO: Simplify after https://github.com/rust-lang/rust/pull/145882 lands. -->
611+
```rust,edition2024,compile_fail,E0716
612+
# fn temp() {}
613+
// As above.
614+
format_args!("{:?}{:?}", (), { &temp() }); // ERROR
615+
```
616+
600617
r[destructors.forget]
601618
## Not running destructors
602619

@@ -621,14 +638,14 @@ There is one additional case to be aware of: when a panic reaches a [non-unwindi
621638

622639
[Assignment]: expressions/operator-expr.md#assignment-expressions
623640
[binding modes]: patterns.md#binding-modes
641+
[borrow macro call]: expr.borrow-macros
624642
[closure]: types/closure.md
625643
[destructors]: destructors.md
626644
[expression]: expressions.md
627645
[identifier pattern]: patterns.md#identifier-patterns
628646
[initialized]: glossary.md#initialized
629647
[interior mutability]: interior-mutability.md
630648
[lazy boolean expression]: expressions/operator-expr.md#lazy-boolean-operators
631-
[macro invocation]: macros.md#macro-invocation
632649
[non-unwinding ABI boundary]: items/functions.md#unwinding
633650
[panic]: panic.md
634651
[place context]: expressions.md#place-expressions-and-value-expressions
@@ -658,12 +675,19 @@ There is one additional case to be aware of: when a panic reaches a [non-unwindi
658675
[array repeat operands]: expr.array.repeat-operand
659676
[async block expression]: expr.block.async
660677
[block expression]: expressions/block-expr.md
661-
[borrow expression]: expressions/operator-expr.md#borrow-operators
678+
[borrow operator]: expr.operator.borrow
679+
[borrow]: expr.operator.borrow
662680
[cast expression]: expressions/operator-expr.md#type-cast-expressions
663681
[dereference expression]: expressions/operator-expr.md#the-dereference-operator
682+
[extended]: destructors.scope.lifetime-extension
664683
[field expression]: expressions/field-expr.md
665684
[indexing expression]: expressions/array-expr.md#array-and-slice-indexing-expressions
666685
[struct expression]: expressions/struct-expr.md
686+
[super macro call]: expr.super-macros
687+
[super operands]: expr.super-macros
688+
[super temporaries]: expr.super-macros
689+
[temporary scope]: destructors.scope.temporary
690+
[temporary scopes]: destructors.scope.temporary
667691
[tuple expression]: expressions/tuple-expr.md#tuple-expressions
668692
[tuple indexing expression]: expressions/tuple-expr.md#tuple-indexing-expressions
669693

@@ -675,6 +699,3 @@ There is one additional case to be aware of: when a panic reaches a [non-unwindi
675699
[`match`]: expressions/match-expr.md
676700
[`while let`]: expressions/loop-expr.md#while-let-patterns
677701
[`while`]: expressions/loop-expr.md#predicate-loops
678-
679-
[`pin!`]: std::pin::pin
680-
[`format_args!`]: core::format_args

src/expressions.md

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,86 @@ When using a value expression in most place expression contexts, a temporary unn
255255
The expression evaluates to that location instead, except if [promoted] to a `static`.
256256
The [drop scope] of the temporary is usually the end of the enclosing statement.
257257

258+
r[expr.super-macros]
259+
### Super macros
260+
261+
r[expr.super-macros.intro]
262+
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*.
263+
264+
> [!NOTE]
265+
> 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].
266+
267+
r[expr.super-macros.format_args]
268+
#### `format_args!`
269+
270+
r[expr.super-macros.format_args.super-operands]
271+
Except for the format string argument, all arguments passed to [`format_args!`] are *super operands*.
272+
273+
<!-- TODO: Simplify after https://github.com/rust-lang/rust/pull/145882 lands. -->
274+
```rust,edition2024
275+
# fn temp() -> String { String::from("") }
276+
// Due to the call being an extending expression and the argument
277+
// being a super operand, the inner block is an extending expression,
278+
// so the scope of the temporary created in its trailing expression
279+
// is extended.
280+
let _ = format_args!("{:?}{}", (), { &temp() }); // OK
281+
```
282+
283+
r[expr.super-macros.format_args.super-temporaries]
284+
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*.
285+
286+
<!-- TODO: Simplify after https://github.com/rust-lang/rust/pull/145882 lands. -->
287+
```rust
288+
# fn temp() -> String { String::from("") }
289+
let x = format_args!("{}{}", temp(), temp());
290+
x; // <-- The temporaries are extended, allowing use here.
291+
```
292+
293+
The expansion of a call to [`format_args!`] sometimes creates other internal *super temporaries*.
294+
295+
```rust,compile_fail,E0716
296+
let x = {
297+
// This call creates an internal temporary.
298+
let x = format_args!("{:?}", 0);
299+
x // <-- The temporary is extended, allowing its use here.
300+
}; // <-- The temporary is dropped here.
301+
x; // ERROR
302+
```
303+
304+
```rust
305+
// This call doesn't create an internal temporary.
306+
let x = { let x = format_args!("{}", 0); x };
307+
x; // OK
308+
```
309+
310+
> [!NOTE]
311+
> The details of when [`format_args!`] does or does not create internal temporaries are currently unspecified.
312+
313+
r[expr.super-macros.pin]
314+
#### `pin!`
315+
316+
r[expr.super-macros.pin.super-operands]
317+
The argument to [`pin!`] is a *super operand*.
318+
319+
```rust,edition2024
320+
# use core::pin::pin;
321+
# fn temp() {}
322+
// As above for `format_args!`.
323+
let _ = pin!({ &temp() }); // OK
324+
```
325+
326+
r[expr.super-macros.pin.super-temporaries]
327+
The argument to [`pin!`] is a [value expression context] and creates a *super temporary*.
328+
329+
```rust
330+
# use core::pin::pin;
331+
# fn temp() {}
332+
// The argument is evaluated into a super temporary.
333+
let x = pin!(temp());
334+
// The temporary is extended, allowing its use here.
335+
x; // OK
336+
```
337+
258338
r[expr.implicit-borrow]
259339
### Implicit Borrows
260340

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

289370
r[expr.overload]
290371
## Overloading Traits
@@ -311,30 +392,39 @@ They are never allowed before:
311392
[`Copy`]: special-types-and-traits.md#copy
312393
[`Drop`]: special-types-and-traits.md#drop
313394
[`if let`]: expressions/if-expr.md#if-let-patterns
395+
[`format_args!`]: core::format_args
396+
[`pin!`]: core::pin::pin
314397
[`Sized`]: special-types-and-traits.md#sized
315398
[`while let`]: expressions/loop-expr.md#while-let-patterns
316399
[array expressions]: expressions/array-expr.md
317400
[array indexing]: expressions/array-expr.md#array-and-slice-indexing-expressions
318401
[assign]: expressions/operator-expr.md#assignment-expressions
319402
[block expressions]: expressions/block-expr.md
320403
[borrow]: expressions/operator-expr.md#borrow-operators
404+
[borrow expressions]: expr.operator.borrow
321405
[call expressions]: expressions/call-expr.md
322406
[comparison]: expressions/operator-expr.md#comparison-operators
323407
[compound assignment]: expressions/operator-expr.md#compound-assignment-expressions
324408
[deref]: expressions/operator-expr.md#the-dereference-operator
325409
[destructors]: destructors.md
326410
[drop scope]: destructors.md#drop-scopes
411+
[extended]: destructors.scope.lifetime-extension
412+
[extending expression]: destructors.scope.lifetime-extension.exprs
413+
[extending expressions]: destructors.scope.lifetime-extension.exprs
327414
[field]: expressions/field-expr.md
328415
[functional update]: expressions/struct-expr.md#functional-update-syntax
329416
[implicit borrow]: #implicit-borrows
417+
[implicitly borrowed]: expr.implicit-borrow
330418
[implicitly mutably borrowed]: #implicit-borrows
331419
[interior mutability]: interior-mutability.md
332420
[let statement]: statements.md#let-statements
421+
[macro invocations]: macro.invocation
333422
[match]: expressions/match-expr.md
334423
[method-call]: expressions/method-call-expr.md
335424
[Mutable `static` items]: items/static-items.md#mutable-statics
336425
[Outer attributes]: attributes.md
337426
[paths]: expressions/path-expr.md
427+
[place expression contexts]: expr.place-value
338428
[promoted]: destructors.md#constant-promotion
339429
[Range]: expressions/range-expr.md
340430
[raw borrow]: expressions/operator-expr.md#raw-borrow-operators
@@ -344,10 +434,16 @@ They are never allowed before:
344434
[static variables]: items/static-items.md
345435
[struct]: expressions/struct-expr.md
346436
[Structs]: expr.struct
437+
[temporaries]: expr.temporary
438+
[temporary scope]: destructors.scope.temporary
439+
[temporary scopes]: destructors.scope.temporary
440+
[temporary]: expr.temporary
347441
[Temporary values]: #temporaries
348442
[tuple expressions]: expressions/tuple-expr.md
349443
[Tuple structs]: items.struct.tuple
350444
[Tuples]: expressions/tuple-expr.md
351445
[Underscores]: expressions/underscore-expr.md
352446
[Unit structs]: items.struct.unit
447+
[value expression context]: expr.place-value
448+
[value expression]: expr.place-value
353449
[Variables]: variables.md

0 commit comments

Comments
 (0)