Skip to content

Commit 268200d

Browse files
authored
Merge pull request #1980 from dianne/extending-macros
Specify lifetime extension of `pin!` and `format_args!` arguments
2 parents e5a1af4 + abedccf commit 268200d

File tree

2 files changed

+142
-10
lines changed

2 files changed

+142
-10
lines changed

src/destructors.md

Lines changed: 43 additions & 10 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
@@ -474,11 +470,13 @@ let &ref x = &*&temp(); // OK
474470
r[destructors.scope.lifetime-extension.exprs]
475471
#### Extending based on expressions
476472

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

480477
* The initializer expression.
481-
* 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.
482480
* The operand(s) of an extending [array][array expression], [cast][cast
483481
expression], [braced struct][struct expression], or [tuple][tuple expression]
484482
expression.
@@ -490,8 +488,11 @@ expression which is one of the following:
490488
So the borrow expressions in `&mut 0`, `(&1, &mut 2)`, and `Some(&mut 3)`
491489
are all extending expressions. The borrows in `&0 + &1` and `f(&mut 0)` are not.
492490

493-
The operand of any extending borrow expression has its temporary scope
494-
extended.
491+
r[destructors.scope.lifetime-extension.exprs.borrows]
492+
The operand of an extending [borrow] expression has its [temporary scope] [extended].
493+
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].
495496

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

505506
```rust,edition2024
507+
# use core::pin::pin;
506508
# use core::sync::atomic::{AtomicU64, Ordering::Relaxed};
507509
# static X: AtomicU64 = AtomicU64::new(0);
508-
# struct S;
510+
# #[derive(Debug)] struct S;
509511
# impl Drop for S { fn drop(&mut self) { X.fetch_add(1, Relaxed); } }
510512
# const fn temp() -> S { S }
511513
let x = &temp(); // Operand of borrow.
@@ -528,6 +530,14 @@ let x = if true { &temp() } else { &temp() };
528530
# x;
529531
let x = match () { _ => &temp() }; // `match` arm expression.
530532
# x;
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+
# // FIXME: Simplify after this PR lands:
538+
# // <https://github.com/rust-lang/rust/pull/145882>.
539+
let x = format_args!("{:?}{:?}", (), temp()); // As above.
540+
# x;
531541
//
532542
// All of the temporaries above are still live here.
533543
# assert_eq!(0, X.load(Relaxed));
@@ -587,6 +597,23 @@ let x = 'a: { break 'a &temp() }; // ERROR
587597
# x;
588598
```
589599

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+
<!-- FIXME: 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+
590617
r[destructors.forget]
591618
## Not running destructors
592619

@@ -647,12 +674,18 @@ There is one additional case to be aware of: when a panic reaches a [non-unwindi
647674
[array repeat operands]: expr.array.repeat-operand
648675
[async block expression]: expr.block.async
649676
[block expression]: expressions/block-expr.md
650-
[borrow expression]: expressions/operator-expr.md#borrow-operators
677+
[borrow]: expr.operator.borrow
651678
[cast expression]: expressions/operator-expr.md#type-cast-expressions
652679
[dereference expression]: expressions/operator-expr.md#the-dereference-operator
680+
[extended]: destructors.scope.lifetime-extension
653681
[field expression]: expressions/field-expr.md
654682
[indexing expression]: expressions/array-expr.md#array-and-slice-indexing-expressions
655683
[struct expression]: expressions/struct-expr.md
684+
[super macro call]: expr.super-macros
685+
[super operands]: expr.super-macros
686+
[super temporaries]: expr.super-macros
687+
[temporary scope]: destructors.scope.temporary
688+
[temporary scopes]: destructors.scope.temporary
656689
[tuple expression]: expressions/tuple-expr.md#tuple-expressions
657690
[tuple indexing expression]: expressions/tuple-expr.md#tuple-indexing-expressions
658691

src/expressions.md

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,92 @@ 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+
<!-- FIXME: Remove after https://github.com/rust-lang/rust/pull/145882 lands. -->
274+
> [!NOTE]
275+
> When there is only one placeholder, `rustc` does not yet treat the corresponding argument as a super operand. This is a bug.
276+
>
277+
> For details, see Rust issue [#145880](https://github.com/rust-lang/rust/issues/145880).
278+
279+
<!-- FIXME: Simplify after https://github.com/rust-lang/rust/pull/145882 lands. -->
280+
```rust,edition2024
281+
# fn temp() -> String { String::from("") }
282+
// Due to the call being an extending expression and the argument
283+
// being a super operand, the inner block is an extending expression,
284+
// so the scope of the temporary created in its trailing expression
285+
// is extended.
286+
let _ = format_args!("{:?}{}", (), { &temp() }); // OK
287+
```
288+
289+
r[expr.super-macros.format_args.super-temporaries]
290+
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*.
291+
292+
<!-- FIXME: Simplify after https://github.com/rust-lang/rust/pull/145882 lands. -->
293+
```rust
294+
# fn temp() -> String { String::from("") }
295+
let x = format_args!("{}{}", temp(), temp());
296+
x; // <-- The temporaries are extended, allowing use here.
297+
```
298+
299+
The expansion of a call to [`format_args!`] sometimes creates other internal *super temporaries*.
300+
301+
```rust,compile_fail,E0716
302+
let x = {
303+
// This call creates an internal temporary.
304+
let x = format_args!("{:?}", 0);
305+
x // <-- The temporary is extended, allowing its use here.
306+
}; // <-- The temporary is dropped here.
307+
x; // ERROR
308+
```
309+
310+
```rust
311+
// This call doesn't create an internal temporary.
312+
let x = { let x = format_args!("{}", 0); x };
313+
x; // OK
314+
```
315+
316+
> [!NOTE]
317+
> The details of when [`format_args!`] does or does not create internal temporaries are currently unspecified.
318+
319+
r[expr.super-macros.pin]
320+
#### `pin!`
321+
322+
r[expr.super-macros.pin.super-operands]
323+
The argument to [`pin!`] is a *super operand*.
324+
325+
```rust,edition2024
326+
# use core::pin::pin;
327+
# fn temp() {}
328+
// As above for `format_args!`.
329+
let _ = pin!({ &temp() }); // OK
330+
```
331+
332+
r[expr.super-macros.pin.super-temporaries]
333+
The argument to [`pin!`] is a [value expression context] and creates a *super temporary*.
334+
335+
```rust
336+
# use core::pin::pin;
337+
# fn temp() {}
338+
// The argument is evaluated into a super temporary.
339+
let x = pin!(temp());
340+
// The temporary is extended, allowing its use here.
341+
x; // OK
342+
```
343+
258344
r[expr.implicit-borrow]
259345
### Implicit Borrows
260346

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

289376
r[expr.overload]
290377
## Overloading Traits
@@ -311,6 +398,8 @@ They are never allowed before:
311398
[`Copy`]: special-types-and-traits.md#copy
312399
[`Drop`]: special-types-and-traits.md#drop
313400
[`if let`]: expressions/if-expr.md#if-let-patterns
401+
[`format_args!`]: core::format_args
402+
[`pin!`]: core::pin::pin
314403
[`Sized`]: special-types-and-traits.md#sized
315404
[`while let`]: expressions/loop-expr.md#while-let-patterns
316405
[array expressions]: expressions/array-expr.md
@@ -324,17 +413,23 @@ They are never allowed before:
324413
[deref]: expressions/operator-expr.md#the-dereference-operator
325414
[destructors]: destructors.md
326415
[drop scope]: destructors.md#drop-scopes
416+
[extended]: destructors.scope.lifetime-extension
417+
[extending expression]: destructors.scope.lifetime-extension.exprs
418+
[extending expressions]: destructors.scope.lifetime-extension.exprs
327419
[field]: expressions/field-expr.md
328420
[functional update]: expressions/struct-expr.md#functional-update-syntax
329421
[implicit borrow]: #implicit-borrows
422+
[implicitly borrowed]: expr.implicit-borrow
330423
[implicitly mutably borrowed]: #implicit-borrows
331424
[interior mutability]: interior-mutability.md
332425
[let statement]: statements.md#let-statements
426+
[macro invocations]: macro.invocation
333427
[match]: expressions/match-expr.md
334428
[method-call]: expressions/method-call-expr.md
335429
[Mutable `static` items]: items/static-items.md#mutable-statics
336430
[Outer attributes]: attributes.md
337431
[paths]: expressions/path-expr.md
432+
[place expression contexts]: expr.place-value
338433
[promoted]: destructors.md#constant-promotion
339434
[Range]: expressions/range-expr.md
340435
[raw borrow]: expressions/operator-expr.md#raw-borrow-operators
@@ -344,10 +439,14 @@ They are never allowed before:
344439
[static variables]: items/static-items.md
345440
[struct]: expressions/struct-expr.md
346441
[Structs]: expr.struct
442+
[temporaries]: expr.temporary
443+
[temporary scopes]: destructors.scope.temporary
347444
[Temporary values]: #temporaries
348445
[tuple expressions]: expressions/tuple-expr.md
349446
[Tuple structs]: items.struct.tuple
350447
[Tuples]: expressions/tuple-expr.md
351448
[Underscores]: expressions/underscore-expr.md
352449
[Unit structs]: items.struct.unit
450+
[value expression context]: expr.place-value
451+
[value expression]: expr.place-value
353452
[Variables]: variables.md

0 commit comments

Comments
 (0)