Skip to content

Conversation

@frank-king
Copy link
Contributor

@frank-king frank-king commented Apr 13, 2025

This PR implements part of #130494. It supports pin-project in pattern matching for &pin mut|const T.

Pin-projection by field access (i.e. &pin mut|const place.field) is not fully supported yet since pinned-borrow is not ready (#135731).

CC @traviscross

@rustbot
Copy link
Collaborator

rustbot commented Apr 13, 2025

r? @compiler-errors

rustbot has assigned @compiler-errors.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added A-attributes Area: Attributes (`#[…]`, `#![…]`) S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Apr 13, 2025
@rust-log-analyzer

This comment has been minimized.

@bors
Copy link
Collaborator

bors commented Apr 18, 2025

☔ The latest upstream changes (presumably #139996) made this pull request unmergeable. Please resolve the merge conflicts.

@frank-king frank-king force-pushed the feature/pin-project branch from 0ad1543 to e9c97df Compare April 20, 2025 12:53
@rustbot rustbot added the F-autodiff `#![feature(autodiff)]` label Apr 20, 2025
@rust-log-analyzer

This comment has been minimized.

@frank-king frank-king force-pushed the feature/pin-project branch 3 times, most recently from 24e0b14 to aa27a06 Compare April 20, 2025 13:47
@frank-king frank-king changed the title WIP: implement pin-project in pattern matching for &pin mut|const T Implement pin-project in pattern matching for &pin mut|const T Apr 20, 2025
@rust-log-analyzer

This comment has been minimized.

@frank-king frank-king force-pushed the feature/pin-project branch from aa27a06 to c9ca4f8 Compare April 20, 2025 14:22
@rust-log-analyzer

This comment has been minimized.

@frank-king frank-king marked this pull request as ready for review April 20, 2025 14:39
@rustbot
Copy link
Collaborator

rustbot commented Apr 20, 2025

Some changes occurred to the CTFE machinery

cc @RalfJung, @oli-obk, @lcnr

Some changes occurred in match checking

cc @Nadrieril

Some changes occurred in compiler/rustc_codegen_ssa

cc @WaffleLapkin

Some changes occurred in src/tools/clippy

cc @rust-lang/clippy

Some changes occurred to MIR optimizations

cc @rust-lang/wg-mir-opt

The Miri subtree was changed

cc @rust-lang/miri

Some changes occurred in compiler/rustc_monomorphize/src/partitioning/autodiff.rs

cc @ZuseZ4

rust-analyzer is developed in its own repository. If possible, consider making this change to rust-lang/rust-analyzer instead.

cc @rust-lang/rust-analyzer

Some changes occurred in exhaustiveness checking

cc @Nadrieril

Some changes occurred in match lowering

cc @Nadrieril

Some changes occurred to the CTFE / Miri interpreter

cc @rust-lang/miri

Some changes occurred in compiler/rustc_codegen_cranelift

cc @bjorn3

@oli-obk oli-obk added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Apr 22, 2025
@Nadrieril
Copy link
Member

It seems like you're implementing a sort of match ergonomics through &pin? What stage is this feature at?

I think pinnedness should be part of the binding mode instead of a separate notion. I haven't looked deeply yet but presumably this will raise questions similar to deref patterns and the recent match ergonomics changes.

cc @dianne

@dianne
Copy link
Contributor

dianne commented Apr 24, 2025

+1 to representing pinnedness as part of the binding mode.

Regarding match ergonomics interactions, I think we'll either want explicit &pin patterns too or logic for using & to match on pinned refs (sorry if that's planned already! I couldn't find it). Writing out the &s leading up to a binding enables making by-value bindings of Copy types; there's not a general way to do that in the pattern without explicit & patterns. Likewise, some patterns (such as bindings) don't automatically peel references from the scrutinee, so &pin could be useful there too.
The diagnostic pretty-printing for exhaustiveness checking uses the &pin const and &pin mut syntax in patterns; without explicit &pin const and &pin mut patterns, those might need adjusting.

Regarding deref patterns interactions, we'll probably also want pin ergonomics for deref patterns eventually. I don't think that'll be a problem, if I understand what Pin<Ptr> means (morally, their pointee's address not changing means calling Deref::deref on a Pin<Ptr> returns a reference that could be wrapped in a pin, right? I'm not too familiar with pinning). Edit: I did a bit more reading; that invariant does seem to exist, so pin-projection ergonomics for deref patterns should be fine conceptually (I think). Not sure how it'd look implementation-wise, since DerefMut wouldn't always work. We'd morally want something more like matching through Pin::as_deref_mut than matching through DerefMut::deref_mut. I'm thinking we could call <Ptr as DerefMut>::deref_mut(&mut pin.pinned) and trust that pinnedness in binding modes will keep everything safe.

@dianne
Copy link
Contributor

dianne commented Apr 24, 2025

Another ergonomics question: is there a way to get a non-pinned by-shared-ref binding when matching through &pin const T or &pin mut T if no intervening types implement Unpin? If I understand correctly, this PR would always result in &pin const bindings in that case. I'm not sure this is an issue (at least not one that needs to be designed around) but I think that's the one case where the binding could be either &pin const or &, so there's a slight awkwardness when the user wants a reference and has to use &* or Pin::get_ref.

@frank-king
Copy link
Contributor Author

frank-king commented Apr 26, 2025

Regarding match ergonomics interactions, I think we'll either want explicit &pin patterns too or logic for using & to match on pinned refs

Does "match ergonomics" refer to rfcs#2005? I see it has been stabilized in 2018, and I'm not quite familiar with "ancient" Rust before (I started to learn Rust in 2021).

My intuition is just based on the crate pin_project. Take an example from its doc:

use std::pin::Pin;
use pin_project::pin_project;

#[pin_project(project = EnumProj)]
enum Enum<T, U> {
    Pinned(#[pin] T),
    Unpinned(U),
}

impl<T, U> Enum<T, U> {
    fn method(self: Pin<&mut Self>) {
        match self.project() {
            EnumProj::Pinned(x) => {
                let _: Pin<&mut T> = x;
            }
            EnumProj::Unpinned(y) = {
                let _: &mut U = y;
            }
        }
    }
}

It uses the #[pin] attribute to determine whether to project a field to a pinned reference Pin<&{mut} T> or a normal reference &{mut} T. That's because the proc-macro doesn't know whether such a field is Unpin. However, rustc knows it (from the U: Unpin trait bound, I think), so we can leverage such information to determine whether to project to a pinned reference or a normal reference. (That may conflict with your words "expected might not be inferred at this point; it's often not known until unifying it with the pattern's type" in #139751 (comment), and please correct me if I'm wrong)

With pin_ergonomics enabled, I expect it to be:

#![feature(pin_ergonomics)]

enum Enum<T, U> {
    // `#[pin]` is no longer needed, as we can infer from the trait bound whether `T` is `Unpin`.
    Pinned(T),
    Unpinned(U),
}

// `U: Unpin` is needed to inform the compiler that `U` can be projected to a normal reference.
impl<T, U: Unpin> Enum<T, U> {
    fn method(&pin mut self) {
        // `self.projection()` is no longer needed, as the compiler
       // would understand how to project a `&pin mut Enum<T, U>`
        match self {
            // `EnumProj` is no longer needed
            Enum::Pinned(x) => {
                // for `T: ?Unpin`, it is projected to `&pin mut T`
                let _: &pin mut T = x;
            }
            Enum::Unpinned(y) = {
                // for `U: Unpin`, it is projected to `&mut U`
                let _: &mut U = y;
            }
        }
    } 
}

That's how I implemented this PR.

@dianne
Copy link
Contributor

dianne commented Apr 26, 2025

Does "match ergonomics" refer to rfcs#2005? I see it has been stabilized in 2018, and I'm not quite familiar with "ancient" Rust before (I started to learn Rust in 2021).

That RFC is indeed pretty old, and isn't quite what's implemented in Rust 20241. I'm using "match ergonomics" loosely to mean a couple things (sorry for the jargon!):

  • The ability to omit explicit reference patterns when matching on reference types; this allows binding by reference without explicit binding mode annotations. As I understand it, that is what this PR implements (with the addition of by-pinned-reference bindings).
  • The ability to add explicit reference patterns to bind Copy types by-value. Currently in Rust 2024, this requires explicitly writing out all reference patterns leading up to a binding. Tracking Issue for match ergonomics 2024 (RFC 3627) #123076 is tracking changes to allow more flexibility with where reference patterns are written, for more "ergonomic" control of the binding mode.

From what I could tell, this PR supports the former of these but not the latter; for consistency with how matching on normal references works, I'd expect to be able to use explicit reference patterns to match on &pin (const|mut) T as well. Plus, that syntax is used (I think) by the match exhaustiveness diagnostics this PR implements for pinned refs, and I'd expect any patterns it outputs to be writable by users too.

However, rustc knows it (from the U: Unpin trait bound, I think), so we can leverage such information to determine whether to project to a pinned reference or a normal reference. (That may conflict with your words "expected might not be inferred at this point; it's often not known until unifying it with the pattern's type" in #139751 (comment), and please correct me if I'm wrong)

It should indeed be possible to utilize the U: Unpin trait bound, and at least as far as I can tell, that does seem like the smoothest way to do it from a user-facing perspective2. The tricky thing I was referring to is if you're matching on something that doesn't have a fully specified type at the point you're matching on it, rust's type-checker will try to infer the type from the patterns you use. However, it doesn't know what type the pattern has until the big match on the pattern's kind field. In this case, expected will be an unknown "inference variable" (or have inference variables in it) at the point you ask whether it's Unpin, which (in this specific case of expected being uninferred) will always conservatively return false even if you'll later learn that expected is Unpin once you learn the pattern's type.

There might be additional subtleties/complications I'm not aware of, of course. I'm not deeply familiar with the trait solver, so I'm not totally sure how unambiguous you can guarantee its results to be. Hence my suggestion to raise an error when there's ambiguities.

Footnotes

  1. Rust 2024's current match ergonomics for references is specified in Tracking issue for Rust 2024: Match ergonomics rules 1C/2C #131414 (edit: also the edition guide as pointed out below). This was designed to be future-compatible with Tracking Issue for match ergonomics 2024 (RFC 3627) #123076 but easier to stabilize in time for the edition.

  2. The one gotcha I can think of besides what I said before is that if you have a type parameter T, you have to assume it's not Unpin when type-checking, but if you perform automatic source code transformations to substitute in a type that is Unpin, it can result in changes in the pinnedness of bindings, which can cause type errors. Thus substituting in a specific type for a type parameter at the source code level would no longer be a well-typedness-preserving operation. But I'm not sure there's really a way around that other than requiring explicit annotations. Tracking Issue for match ergonomics 2024 (RFC 3627) #123076 has a similar issue when trying to get it working on pre-2024 Editions in a backwards-compatible way.

@traviscross

This comment was marked as duplicate.

@rust-log-analyzer

This comment has been minimized.

@frank-king
Copy link
Contributor Author

@rustbot ready

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Oct 31, 2025
@traviscross
Copy link
Contributor

@bors2 try jobs=test-various

@rust-bors

This comment has been minimized.

rust-bors bot added a commit that referenced this pull request Oct 31, 2025
Implement pin-project in pattern matching for `&pin mut|const T`

try-job: test-various
@rust-bors
Copy link

rust-bors bot commented Oct 31, 2025

☀️ Try build successful (CI)
Build commit: 28f0dba (28f0dbaea7526229cf2653f4cd50782159b61f82, parent: 647f1536d2f50b203ca9a67396225c4f7771e1c8)

@traviscross
Copy link
Contributor

@bors r=Nadrieril,traviscross rollup

@bors
Copy link
Collaborator

bors commented Oct 31, 2025

📌 Commit d8ace32 has been approved by Nadrieril,traviscross

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Oct 31, 2025
Zalathar added a commit to Zalathar/rust that referenced this pull request Nov 1, 2025
…Nadrieril,traviscross

Implement pin-project in pattern matching for `&pin mut|const T`

This PR implements part of rust-lang#130494. It supports pin-project in pattern matching for `&pin mut|const T`.

~Pin-projection by field access (i.e. `&pin mut|const place.field`) is not fully supported yet since pinned-borrow is not ready (rust-lang#135731).~

CC `@traviscross`
Zalathar added a commit to Zalathar/rust that referenced this pull request Nov 1, 2025
…Nadrieril,traviscross

Implement pin-project in pattern matching for `&pin mut|const T`

This PR implements part of rust-lang#130494. It supports pin-project in pattern matching for `&pin mut|const T`.

~Pin-projection by field access (i.e. `&pin mut|const place.field`) is not fully supported yet since pinned-borrow is not ready (rust-lang#135731).~

CC ``@traviscross``
Zalathar added a commit to Zalathar/rust that referenced this pull request Nov 1, 2025
…Nadrieril,traviscross

Implement pin-project in pattern matching for `&pin mut|const T`

This PR implements part of rust-lang#130494. It supports pin-project in pattern matching for `&pin mut|const T`.

~Pin-projection by field access (i.e. `&pin mut|const place.field`) is not fully supported yet since pinned-borrow is not ready (rust-lang#135731).~

CC ```@traviscross```
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Nov 1, 2025
…Nadrieril,traviscross

Implement pin-project in pattern matching for `&pin mut|const T`

This PR implements part of rust-lang#130494. It supports pin-project in pattern matching for `&pin mut|const T`.

~Pin-projection by field access (i.e. `&pin mut|const place.field`) is not fully supported yet since pinned-borrow is not ready (rust-lang#135731).~

CC ````@traviscross````
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Nov 1, 2025
…Nadrieril,traviscross

Implement pin-project in pattern matching for `&pin mut|const T`

This PR implements part of rust-lang#130494. It supports pin-project in pattern matching for `&pin mut|const T`.

~Pin-projection by field access (i.e. `&pin mut|const place.field`) is not fully supported yet since pinned-borrow is not ready (rust-lang#135731).~

CC `````@traviscross`````
bors added a commit that referenced this pull request Nov 1, 2025
Rollup of 10 pull requests

Successful merges:

 - #135602 (Tweak output of missing lifetime on associated type)
 - #139751 (Implement pin-project in pattern matching for `&pin mut|const T`)
 - #142682 (Update bundled musl to 1.2.5)
 - #148171 (Simplify code to generate line numbers in highlight)
 - #148263 (Unpin `libc` and `rustix` in `compiler` and `rustbook`)
 - #148301 ([rustdoc search] Include extern crates when filtering on `import`)
 - #148330 (Don't require dlltool with the dummy backend on MinGW)
 - #148338 (cleanup: upstream dropped amx-transpose functionality)
 - #148340 (Clippy subtree update)
 - #148343 (`nonpoison::Condvar` should take `MutexGuard` by reference)

r? `@ghost`
`@rustbot` modify labels: rollup
bors added a commit that referenced this pull request Nov 1, 2025
Rollup of 10 pull requests

Successful merges:

 - #135602 (Tweak output of missing lifetime on associated type)
 - #139751 (Implement pin-project in pattern matching for `&pin mut|const T`)
 - #142682 (Update bundled musl to 1.2.5)
 - #148171 (Simplify code to generate line numbers in highlight)
 - #148263 (Unpin `libc` and `rustix` in `compiler` and `rustbook`)
 - #148301 ([rustdoc search] Include extern crates when filtering on `import`)
 - #148330 (Don't require dlltool with the dummy backend on MinGW)
 - #148338 (cleanup: upstream dropped amx-transpose functionality)
 - #148340 (Clippy subtree update)
 - #148343 (`nonpoison::Condvar` should take `MutexGuard` by reference)

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors merged commit dc90606 into rust-lang:master Nov 1, 2025
12 checks passed
@rustbot rustbot added this to the 1.93.0 milestone Nov 1, 2025
rust-timer added a commit that referenced this pull request Nov 1, 2025
Rollup merge of #139751 - frank-king:feature/pin-project, r=Nadrieril,traviscross

Implement pin-project in pattern matching for `&pin mut|const T`

This PR implements part of #130494. It supports pin-project in pattern matching for `&pin mut|const T`.

~Pin-projection by field access (i.e. `&pin mut|const place.field`) is not fully supported yet since pinned-borrow is not ready (#135731).~

CC ``````@traviscross``````
@frank-king frank-king deleted the feature/pin-project branch November 2, 2025 03:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-attributes Area: Attributes (`#[…]`, `#![…]`) disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. I-lang-radar Items that are on lang's radar and will need eventual work or consideration. P-lang-drag-1 Lang team prioritization drag level 1. https://rust-lang.zulipchat.com/#narrow/channel/410516-t-lang S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-clippy Relevant to the Clippy team. T-lang Relevant to the language team T-rustfmt Relevant to the rustfmt team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.