rust/compiler
bors 6421a499a5 Auto merge of #93176 - danielhenrymantilla:stack-pinning-macro, r=m-ou-se
Add a stack-`pin!`-ning macro to `core::pin`.

  - https://github.com/rust-lang/rust/issues/93178

`pin!` allows pinning a value to the stack. Thanks to being implemented in the stdlib, which gives access to `macro` macros, and to the private `.pointer` field of the `Pin` wrapper, [it was recently discovered](https://rust-lang.zulipchat.com/#narrow/stream/187312-wg-async-foundations/topic/pin!.20.E2.80.94.20the.20.22definitive.22.20edition.20.28a.20rhs-compatible.20pin-nin.2E.2E.2E/near/268731241) ([archive link](https://zulip-archive.rust-lang.org/stream/187312-wg-async-foundations/topic/A.20rhs-compatible.20pin-ning.20macro.html#268731241)), contrary to popular belief, that it is actually possible to implement and feature such a macro:

```rust
let foo: Pin<&mut PhantomPinned> = pin!(PhantomPinned);
stuff(foo);
```
or, directly:

```rust
stuff(pin!(PhantomPinned));
```

  - For context, historically, this used to require one of the two following syntaxes:

      - ```rust
        let foo = PhantomPinned;
        pin!(foo);
        stuff(foo);
        ```

      -  ```rust
         pin! {
             let foo = PhantomPinned;
         }
         stuff(foo);
         ```

This macro thus allows, for instance, doing things like:

```diff
fn block_on<T>(fut: impl Future<Output = T>) -> T {
    // Pin the future so it can be polled.
-   let mut fut = Box::pin(fut);
+   let mut fut = pin!(fut);

    // Create a new context to be passed to the future.
    let t = thread::current();
    let waker = Arc::new(ThreadWaker(t)).into();
    let mut cx = Context::from_waker(&waker);

    // Run the future to completion.
    loop {
        match fut.as_mut().poll(&mut cx) {
            Poll::Ready(res) => return res,
            Poll::Pending => thread::park(),
        }
    }
}
```

  - _c.f._, https://doc.rust-lang.org/1.58.1/alloc/task/trait.Wake.html

And so on, and so forth.

I don't think such an API can get better than that, barring full featured language support (`&pin` references or something), so I see no reason not to start experimenting with featuring this in the stdlib already 🙂

  - cc `@rust-lang/wg-async-foundations` \[EDIT: this doesn't seem to have pinged anybody 😩, thanks `@yoshuawuyts` for the real ping\]

r? `@joshtriplett`

___

# Docs preview

https://user-images.githubusercontent.com/9920355/150605731-1f45c2eb-c9b0-4ce3-b17f-2784fb75786e.mp4

___

# Implementation

The implementation ends up being dead simple (so much it's embarrassing):

```rust
pub macro pin($value:expr $(,)?) {
    Pin { pointer: &mut { $value } }
}
```

_and voilà_!

  - The key for it working lies in [the rules governing the scope of anonymous temporaries](https://doc.rust-lang.org/1.58.1/reference/destructors.html#temporary-lifetime-extension).

<details><summary>Comments and context</summary>

This is `Pin::new_unchecked(&mut { $value })`, so, for starters, let's
review such a hypothetical macro (that any user-code could define):
```rust
macro_rules! pin {( $value:expr ) => (
    match &mut { $value } { at_value => unsafe { // Do not wrap `$value` in an `unsafe` block.
        $crate::pin::Pin::<&mut _>::new_unchecked(at_value)
    }}
)}
```

Safety:
  - `type P = &mut _`. There are thus no pathological `Deref{,Mut}` impls that would break `Pin`'s invariants.
  - `{ $value }` is braced, making it a _block expression_, thus **moving** the given `$value`, and making it _become an **anonymous** temporary_.
    By virtue of being anonynomous, it can no longer be accessed, thus preventing any attemps to `mem::replace` it or `mem::forget` it, _etc._

This gives us a `pin!` definition that is sound, and which works, but only in certain scenarios:

  - If the `pin!(value)` expression is _directly_ fed to a function call:
    `let poll = pin!(fut).poll(cx);`

  - If the `pin!(value)` expression is part of a scrutinee:

    ```rust
    match pin!(fut) { pinned_fut => {
        pinned_fut.as_mut().poll(...);
        pinned_fut.as_mut().poll(...);
    }} // <- `fut` is dropped here.
    ```

Alas, it doesn't work for the more straight-forward use-case: `let` bindings.

```rust
let pinned_fut = pin!(fut); // <- temporary value is freed at the end of this statement
pinned_fut.poll(...) // error[E0716]: temporary value dropped while borrowed
                     // note: consider using a `let` binding to create a longer lived value
```

  - Issues such as this one are the ones motivating https://github.com/rust-lang/rfcs/pull/66

This makes such a macro incredibly unergonomic in practice, and the reason most macros out there had to take the path of being a statement/binding macro (_e.g._, `pin!(future);`) instead of featuring the more intuitive ergonomics of an expression macro.

Luckily, there is a way to avoid the problem. Indeed, the problem stems from the fact that a temporary is dropped at the end of its enclosing statement when it is part of the parameters given to function call, which has precisely been the case with our `Pin::new_unchecked()`!

For instance,

```rust
let p = Pin::new_unchecked(&mut <temporary>);
```

becomes:

```rust
let p = { let mut anon = <temporary>; &mut anon };
```

However, when using a literal braced struct to construct the value, references to temporaries can then be taken. This makes Rust change the lifespan of such temporaries so that they are, instead, dropped _at the end of the enscoping block_.

For instance,
```rust
let p = Pin { pointer: &mut <temporary> };
```

becomes:

```rust
let mut anon = <temporary>;
let p = Pin { pointer: &mut anon };
```

which is *exactly* what we want.

Finally, we don't hit problems _w.r.t._ the privacy of the `pointer` field, or the unqualified `Pin` name, thanks to `decl_macro`s being _fully_ hygienic (`def_site` hygiene).

</details>

___

# TODO

  - [x] Add compile-fail tests with attempts to break the `Pin` invariants thanks to the macro (_e.g._, try to access the private `.pointer` field, or see what happens if such a pin is used outside its enscoping scope (borrow error));
  - [ ] Follow-up stuff:
      - [ ] Try to experiment with adding `pin!` to the prelude: this may require to be handled with some extra care, as it may lead to issues reminiscent of those of `assert_matches!`: https://github.com/rust-lang/rust/issues/82913
      - [x] Create the tracking issue.
2022-02-15 09:32:03 +00:00
..
rustc remove unused jemallocator crate 2022-01-28 16:56:05 +01:00
rustc_apfloat Stabilize iter::zip. 2021-12-14 18:50:31 -04:00
rustc_arena Remove unused dep from rustc_arena 2022-02-02 17:37:14 +01:00
rustc_ast Auto merge of #93752 - eholk:drop-tracking-break-continue, r=nikomatsakis 2022-02-15 02:27:37 +00:00
rustc_ast_lowering Auto merge of #91403 - cjgillot:inherit-async, r=oli-obk 2022-02-12 21:42:10 +00:00
rustc_ast_passes Auto merge of #93561 - Amanieu:more-unwind-abi, r=nagisa 2022-02-08 03:20:05 +00:00
rustc_ast_pretty Pretty print ItemKind::Use in rustfmt style 2022-02-07 21:51:05 -08:00
rustc_attr Ensure that queries only return Copy types. 2022-02-09 20:07:38 +01:00
rustc_borrowck Rollup merge of #90532 - fee1-dead:improve-const-fn-err-msg, r=oli-obk 2022-02-13 06:44:13 +01:00
rustc_builtin_macros Remove the alt_std_name option 2022-02-11 20:28:38 +01:00
rustc_codegen_cranelift Unconditionally update symbols 2022-02-10 18:27:18 +01:00
rustc_codegen_gcc Unconditionally update symbols 2022-02-10 18:27:18 +01:00
rustc_codegen_llvm Auto merge of #93670 - erikdesjardins:noundef, r=nikic 2022-02-13 00:14:52 +00:00
rustc_codegen_ssa Rollup merge of #93782 - adamgemmell:dev/adagem01/split-pauth, r=Amanieu 2022-02-11 21:48:48 +01:00
rustc_const_eval Report the selection error when possible 2022-02-12 19:24:43 +11:00
rustc_data_structures Call the method fork instead of clone and add proper comments 2022-02-14 12:57:20 -03:00
rustc_driver Remove Config::stderr 2022-02-13 11:49:52 +01:00
rustc_error_codes Auto merge of #91403 - cjgillot:inherit-async, r=oli-obk 2022-02-12 21:42:10 +00:00
rustc_errors add a rustc::query_stability lint 2022-02-01 10:15:59 +01:00
rustc_expand add a rustc::query_stability lint 2022-02-01 10:15:59 +01:00
rustc_feature Update unsafe_pin_internals unstable version. 2022-02-14 19:17:21 +00:00
rustc_fs_util Migrate to 2021 2021-09-20 22:21:42 -04:00
rustc_graphviz eplace usages of vec![].into_iter with [].into_iter 2022-01-09 14:09:25 +11:00
rustc_hir Auto merge of #93938 - BoxyUwU:fix_res_self_ty, r=lcnr 2022-02-14 12:26:43 +00:00
rustc_hir_pretty Rollup merge of #93746 - cjgillot:nodefii, r=nikomatsakis 2022-02-09 14:12:22 +09:00
rustc_incremental add a rustc::query_stability lint 2022-02-01 10:15:59 +01:00
rustc_index implement lint for suspicious auto trait impls 2022-02-01 09:55:19 +01:00
rustc_infer reveal_defining_opaque_types field doesn't exist after rebase 2022-02-14 13:02:22 -03:00
rustc_interface Remove Config::stderr 2022-02-13 11:49:52 +01:00
rustc_lexer Auto merge of #91393 - Julian-Wollersberger:lexer_optimization, r=petrochenkov 2021-12-03 13:20:14 +00:00
rustc_lint change to a struct variant 2022-02-12 11:23:53 +00:00
rustc_lint_defs implement lint for suspicious auto trait impls 2022-02-01 09:55:19 +01:00
rustc_llvm Apply noundef attribute to &T, &mut T, Box<T>, bool 2022-02-05 01:09:52 -05:00
rustc_log Make rustc_log doc test runnable 2022-01-03 22:31:56 -08:00
rustc_macros Make Decodable and Decoder infallible. 2022-01-22 10:38:31 +11:00
rustc_metadata fast_reject: remove StripReferences 2022-02-14 07:37:14 +01:00
rustc_middle Auto merge of #93298 - lcnr:issue-92113, r=cjgillot 2022-02-14 14:47:20 +00:00
rustc_mir_build change to a struct variant 2022-02-12 11:23:53 +00:00
rustc_mir_dataflow rustc_mir_dataflow: use iter::once instead of Some().into_iter 2022-02-03 13:52:26 -07:00
rustc_mir_transform add tainted_by_errors to mir::Body 2022-02-11 12:45:51 -08:00
rustc_monomorphize add a rustc::query_stability lint 2022-02-01 10:15:59 +01:00
rustc_parse Rollup merge of #93595 - compiler-errors:ice-on-lifetime-arg, r=jackh726 2022-02-12 09:26:21 +01:00
rustc_parse_format Migrate to 2021 2021-09-20 22:21:42 -04:00
rustc_passes change to a struct variant 2022-02-12 11:23:53 +00:00
rustc_plugin_impl replace dynamic library module with libloading 2021-12-06 12:03:47 -05:00
rustc_privacy change to a struct variant 2022-02-12 11:23:53 +00:00
rustc_query_impl Auto merge of #93741 - Mark-Simulacrum:global-job-id, r=cjgillot 2022-02-09 18:54:30 +00:00
rustc_query_system Revert "Auto merge of #92007 - oli-obk:lazy_tait2, r=nikomatsakis" 2022-02-11 07:18:06 +00:00
rustc_resolve Auto merge of #93938 - BoxyUwU:fix_res_self_ty, r=lcnr 2022-02-14 12:26:43 +00:00
rustc_save_analysis change to a struct variant 2022-02-12 11:23:53 +00:00
rustc_serialize Drop json::from_reader 2022-02-05 15:07:10 -05:00
rustc_session Remove the alt_std_name option 2022-02-11 20:28:38 +01:00
rustc_span Auto merge of #93176 - danielhenrymantilla:stack-pinning-macro, r=m-ou-se 2022-02-15 09:32:03 +00:00
rustc_symbol_mangling add a rustc::query_stability lint 2022-02-01 10:15:59 +01:00
rustc_target rustc_target: Remove compiler-rt linking hack on Android 2022-02-13 21:22:02 +08:00
rustc_trait_selection Auto merge of #93652 - spastorino:fix-negative-overlap-check-regions, r=nikomatsakis 2022-02-14 18:28:04 +00:00
rustc_traits Reverse parameter to placeholder substitution in chalk results 2022-02-11 21:38:17 +00:00
rustc_ty_utils remove TyS::same_type 2022-02-01 11:21:26 +01:00
rustc_type_ir Revert "Auto merge of #92007 - oli-obk:lazy_tait2, r=nikomatsakis" 2022-02-11 07:18:06 +00:00
rustc_typeck Auto merge of #93752 - eholk:drop-tracking-break-continue, r=nikomatsakis 2022-02-15 02:27:37 +00:00