Skip to content

Implement borrowck for &pin mut|const $place#153693

Draft
frank-king wants to merge 3 commits intorust-lang:mainfrom
frank-king:feature/pin-borrowck
Draft

Implement borrowck for &pin mut|const $place#153693
frank-king wants to merge 3 commits intorust-lang:mainfrom
frank-king:feature/pin-borrowck

Conversation

@frank-king
Copy link
Copy Markdown
Contributor

@frank-king frank-king commented Mar 11, 2026

Part of Pin Ergonomics.

This forbids places to be moved or mutably borrowed ever since they are pinned until they are reassigned (even after the pinned borrows themselves expire).

#![feature(pin_ergnonmics)]
#![allow(incomplete_features)]

#[pin_v2]
struct Foo;

fn foo(mut x: Foo) {
    {
        let _x = &pin mut x; // x is pinned
    } // now the pinned reference expires
    let _x = &mut x; //~ ERROR cannot borrow `x` as mutable because it is pinned
    let _x = x; //~ ERROR cannot move out of `x` because it is pinned
    
    x = Foo; // x is reassigned
    let _x = &mut x; // ok because x is reassigned and not pinned yet
}

Tasks:

  • Add Pins dataflow analysis
  • Improve the diagnostics of pinning errors
  • Test pin pattern matching
  • Forbid &pin borrows for ADTs without #[pin_v2] (for soundness concerns)

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Mar 11, 2026
@rust-log-analyzer

This comment has been minimized.

@frank-king frank-king force-pushed the feature/pin-borrowck branch from f6b714b to 46c66aa Compare April 17, 2026 03:07
@rust-log-analyzer

This comment has been minimized.

@P8L1
Copy link
Copy Markdown

P8L1 commented Apr 27, 2026

I opened a PR against your feature/pin-borrowck branch with a focused fix for the false pinned-borrow diagnostic in pin-coercion.rs:

frank-king#1

It distinguishes persistent user &pin borrows from transient compiler-generated AutoBorrow::Pin adjustments, and includes focused UI regression tests.

@P8L1
Copy link
Copy Markdown

P8L1 commented Apr 28, 2026

I opened a follow-up PR against your feature/pin-borrowck branch for the remaining direct &pin / #[pin_v2] rule:

frank-king#2

It rejects direct user-written &pin mut / &pin const borrows of ADTs that are not #[pin_v2], updates the affected borrowck tests to keep reaching later behavior, and adds focused UI coverage for the new rule.

@rust-log-analyzer
Copy link
Copy Markdown
Collaborator

The job aarch64-gnu-llvm-21-1 failed! Check out the build log: (web) (plain enhanced) (plain)

Click to see the possible cause of the failure (guessed by this bot)
##[endgroup]
Executing "/scripts/stage_2_test_set1.sh"
+ /scripts/stage_2_test_set1.sh
+ '[' 1 == 1 ']'
+ echo 'PR_CI_JOB set; skipping tidy'
+ SKIP_TIDY='--skip tidy'
+ ../x.py --stage 2 test --skip tidy --skip compiler --skip src
PR_CI_JOB set; skipping tidy
##[group]Building bootstrap
    Finished `dev` profile [unoptimized] target(s) in 0.04s
##[endgroup]
---
test [ui] tests/ui/pin-ergonomics/borrow-unpin.rs#pinned ... ok
test [ui] tests/ui/pattern/usefulness/unstable-gated-patterns.rs ... ok
test [ui] tests/ui/pin-ergonomics/borrow-unpin.rs#unpin ... ok
test [ui] tests/ui/pin-ergonomics/coerce-non-pointer-pin.rs ... ok
test [ui] tests/ui/pin-ergonomics/direct-borrow-requires-pin-v2.rs ... ok
test [ui] tests/ui/pin-ergonomics/impl-unpin.rs#adt ... ok
test [ui] tests/ui/pin-ergonomics/impl-unpin.rs#ty_alias ... ok
test [ui] tests/ui/pin-ergonomics/impl-unpin.rs#tait ... ok
test [ui] tests/ui/pin-ergonomics/impl-unpin.rs#assoc ... ok
test [ui] tests/ui/pin-ergonomics/pattern-matching-deref-pattern.rs#normal ... ok
---

---- [pretty] tests/pretty/pin-ergonomics.rs stdout ----
------rustc stdout------------------------------

------rustc stderr------------------------------
error: cannot directly pin an ADT that is not `#[pin_v2]`
##[error]  --> <anon>:27:30
   |
27 |     let mut x: Pin<&mut _> = &pin mut Foo;
   |                              ^^^^^^^^^^^^
   |
note: type defined here
  --> <anon>:8:1
   |
 8 | struct Foo;
   | ^^^^^^^^^^
help: add `#[pin_v2]` here
   |
 8 + #[pin_v2]
 9 | struct Foo;
   |

error: cannot directly pin an ADT that is not `#[pin_v2]`
##[error]  --> <anon>:32:22
   |
32 |     let x: Pin<&_> = &pin const Foo;
   |                      ^^^^^^^^^^^^^^
   |
note: type defined here
  --> <anon>:8:1
   |
 8 | struct Foo;
   | ^^^^^^^^^^
help: add `#[pin_v2]` here
   |
 8 + #[pin_v2]
 9 | struct Foo;
   |

warning: variable does not need to be mutable
##[warning]  --> <anon>:11:12
   |
11 |     fn baz(&pin mut self) {}
   |            -----^^^^^^^^
   |            |
   |            help: remove this `mut`
   |
   = note: `#[warn(unused_mut)]` (part of `#[warn(unused)]`) on by default

warning: variable does not need to be mutable
##[warning]  --> <anon>:15:19
   |
15 |     fn baz_lt<'a>(&'a pin mut self) {}
   |                   ----^^^^^^^^^^^^
   |                   |
   |                   help: remove this `mut`

warning: unused variable: `x`
##[warning]  --> <anon>:37:26
   |
37 | fn patterns<'a>(&pin mut x: &pin mut i32, &pin const y: &'a pin const i32,
   |                          ^ help: if this is intentional, prefix it with an underscore: `_x`
   |
   = note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default

warning: unused variable: `y`
##[warning]  --> <anon>:37:54
   |
37 | fn patterns<'a>(&pin mut x: &pin mut i32, &pin const y: &'a pin const i32,
   |                                                      ^ help: if this is intentional, prefix it with an underscore: `_y`

warning: unused variable: `z`
##[warning]  --> <anon>:38:5
   |
38 |     ref pin mut z: i32, ref pin const w: i32) {}
   |     ^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_z`

warning: unused variable: `w`
##[warning]  --> <anon>:38:25
   |
38 |     ref pin mut z: i32, ref pin const w: i32) {}
   |                         ^^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_w`

error: aborting due to 2 previous errors; 6 warnings emitted


------------------------------------------

error: pretty-printed source does not typecheck
status: exit status: 1
command: env -u RUSTC_LOG_COLOR RUSTC_ICE="0" RUST_BACKTRACE="short" "/checkout/obj/build/aarch64-unknown-linux-gnu/stage2/bin/rustc" "-" "-Zno-codegen" "-Zunstable-options" "--out-dir" "/checkout/obj/build/aarch64-unknown-linux-gnu/test/pretty/pin-ergonomics/pin-ergonomics.pretty-out" "--target=aarch64-unknown-linux-gnu" "-L" "/checkout/obj/build/aarch64-unknown-linux-gnu/test/pretty" "-L" "/checkout/obj/build/aarch64-unknown-linux-gnu/test/pretty/pin-ergonomics/auxiliary.pretty" "-A" "internal_features" "--check-cfg" "cfg(test,FALSE)" "-Crpath" "-Cdebuginfo=0"
stdout: none
--- stderr -------------------------------
error: cannot directly pin an ADT that is not `#[pin_v2]`
##[error]  --> <anon>:27:30
   |
27 |     let mut x: Pin<&mut _> = &pin mut Foo;
   |                              ^^^^^^^^^^^^
   |
note: type defined here
  --> <anon>:8:1
   |
 8 | struct Foo;
   | ^^^^^^^^^^
help: add `#[pin_v2]` here
   |
 8 + #[pin_v2]
 9 | struct Foo;
   |

error: cannot directly pin an ADT that is not `#[pin_v2]`
##[error]  --> <anon>:32:22
   |
32 |     let x: Pin<&_> = &pin const Foo;
   |                      ^^^^^^^^^^^^^^
   |
note: type defined here
  --> <anon>:8:1
   |
 8 | struct Foo;
   | ^^^^^^^^^^
help: add `#[pin_v2]` here
   |
 8 + #[pin_v2]
 9 | struct Foo;
   |

warning: variable does not need to be mutable
##[warning]  --> <anon>:11:12
   |
11 |     fn baz(&pin mut self) {}
   |            -----^^^^^^^^
   |            |
   |            help: remove this `mut`
   |
   = note: `#[warn(unused_mut)]` (part of `#[warn(unused)]`) on by default

warning: variable does not need to be mutable
##[warning]  --> <anon>:15:19
   |
15 |     fn baz_lt<'a>(&'a pin mut self) {}
   |                   ----^^^^^^^^^^^^
   |                   |
   |                   help: remove this `mut`

warning: unused variable: `x`
##[warning]  --> <anon>:37:26
   |
37 | fn patterns<'a>(&pin mut x: &pin mut i32, &pin const y: &'a pin const i32,
   |                          ^ help: if this is intentional, prefix it with an underscore: `_x`
   |
   = note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default

warning: unused variable: `y`
##[warning]  --> <anon>:37:54
   |
37 | fn patterns<'a>(&pin mut x: &pin mut i32, &pin const y: &'a pin const i32,
   |                                                      ^ help: if this is intentional, prefix it with an underscore: `_y`

warning: unused variable: `z`
##[warning]  --> <anon>:38:5
   |
38 |     ref pin mut z: i32, ref pin const w: i32) {}
   |     ^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_z`

warning: unused variable: `w`
##[warning]  --> <anon>:38:25
   |
38 |     ref pin mut z: i32, ref pin const w: i32) {}
   |                         ^^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_w`

error: aborting due to 2 previous errors; 6 warnings emitted
------------------------------------------

---- [pretty] tests/pretty/pin-ergonomics.rs stdout end ----

@P8L1
Copy link
Copy Markdown

P8L1 commented Apr 29, 2026

I looked through the remaining unchecked Test pin pattern matching item.

I think this may already be covered by the existing tests, and I do not see a non-duplicate test that would add meaningful extra coverage right now.

Existing coverage includes:

  • tests/ui/pin-ergonomics/pin-pattern.rs

    • explicit &pin mut / &pin const patterns
    • ref pin mut / ref pin const
    • by-value Copy allowance
    • by-value non-Copy move rejection
    • Unpin demotion behavior
  • tests/ui/pin-ergonomics/pattern-matching.rs

    • #[pin_v2] projection through structs, enums, tuples, arrays, and slices
    • mutable and shared pinned references
    • normal/no-pin_ergonomics revision behavior
  • tests/ui/pin-ergonomics/pattern-matching-deref-pattern.rs

    • pin projection interaction with deref_patterns
  • tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.rs

    • diagnostics for projection through non-#[pin_v2] ADTs
    • let and match pattern coverage
  • tests/ui/pin-ergonomics/user-type-projection.rs

    • user type annotations through projected pin patterns
  • tests/pretty/pin-ergonomics.rs and tests/pretty/pin-ergonomics-hir.rs

    • pretty/HIR printing of pin pattern syntax

Given that, I think the Test pin pattern matching item can probably be marked as done unless reviewers want a very specific additional spelling covered.

The only optional fallback I found would be a focused compile-fail test for explicit &pin mut NonPinProject { .. } / &pin const NonPinProject { .. } on a non-#[pin_v2] ADT, but the same semantic rule already appears to be covered by pattern-matching-mix-deref-pattern.rs.

@P8L1
Copy link
Copy Markdown

P8L1 commented Apr 29, 2026

I also opened a small follow-up PR for the stage-2 pretty-suite failure:

frank-king#3

The failure was in tests/pretty/pin-ergonomics.rs: the pretty test directly pinned plain Foo, but direct pinning of ADTs now correctly requires #[pin_v2].

The follow-up only updates the pretty test fixture and passed locally:

./x test tests/pretty/pin-ergonomics.rs --stage 2 --test-args --force-rerun
./x test tests/pretty/pin-ergonomics-hir.rs --stage 2 --test-args --force-rerun
./x test tests/pretty --stage 2 --test-args pin-ergonomics --force-rerun
./x test tests/ui/pin-ergonomics --stage 2 --test-args --force-rerun
./x test --stage 2 --skip tidy --skip compiler --skip src
git diff --check
./x fmt --check

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants