-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Implement pin-project in pattern matching for &pin mut|const T
#139751
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
rustbot has assigned @compiler-errors. Use |
This comment has been minimized.
This comment has been minimized.
☔ The latest upstream changes (presumably #139996) made this pull request unmergeable. Please resolve the merge conflicts. |
0ad1543
to
e9c97df
Compare
This comment has been minimized.
This comment has been minimized.
24e0b14
to
aa27a06
Compare
&pin mut|const T
&pin mut|const T
This comment has been minimized.
This comment has been minimized.
aa27a06
to
c9ca4f8
Compare
This comment has been minimized.
This comment has been minimized.
Some changes occurred to the CTFE machinery Some changes occurred in match checking cc @Nadrieril Some changes occurred in compiler/rustc_codegen_ssa 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 |
It seems like you're implementing a sort of match ergonomics through 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 |
+1 to representing pinnedness as part of the binding mode. Regarding match ergonomics interactions, I think we'll either want explicit 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 |
Another ergonomics question: is there a way to get a non-pinned by-shared-ref binding when matching through |
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 With #![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. |
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!):
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
It should indeed be possible to utilize the 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
|
This comment was marked as duplicate.
This comment was marked as duplicate.
This already isn't true of Rust due to the behavior of method resolution. E.g.: |
Do you mean something like this? fn f<T: Unpin + Copy>(x: &pin mut T, y: &mut T) {
let &pin mut _x = x; // gets `_x: T`
let &mut _y = y; // like this, which gets `_y: T`
} I think that could be possible and could be implemented in the next PRs (it's an independent feature and requires introducing extra syntax).
If you mind this inconsistency, I can revert this diagnostic syntax and reintroduce it later if any following PRs implement that feature. |
Exactly, yeah! I imagine it'd work just like normal reference patterns in terms of where they can be used and how they affect binding by-ref, and it'd work just like implicitly matching on pinned refs in terms of how they affect pinnedness. Though I don't think it'd need an
I don't personally mind temporary diagnostic inconsistencies for in-development features (but I'm also not a compiler team member, so take whatever I say with a grain of salt). Either way, a Speaking of exhaustiveness, could you add tests for that? Maybe one for non-exhaustive patterns, one for unreachable patterns, and one featuring matches with both pin ergonomics and Oh, and speaking of tests, could you add some for closures that implicitly match on pinned refs as well? In particular, if you move away from allowing builtin derefs on pinned refs, there'll need to be special handling in Footnotes
|
// This is a pin ref pattern. | ||
ty::Adt(adt, args) | ||
if adt.is_pin() | ||
&& args.type_at(0).is_ref() | ||
&& self.tcx.features().pin_ergonomics() => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For pin to appear in a PatKind::Deref
, I think pin_ergonomics
would have to be enabled, so perhaps this should (debug) assert that it's enabled rather than check it. That said, if you're not using built-in derefs, I think patterns on pinned refs shouldn't be represented by PatKind::Deref
. If you haven't already made this change, I think a PatKind::Leaf
pattern with a PatKind::Deref
field might make things work, both here and in MIR lowering? Though there may be a reason I haven't thought of to introduce a new PatKind
.
Per #139751 (comment), if you decide to represent derefs of pinned refs like deref patterns, giving it the same representation in THIR as Pin { .. }
patterns won't work; I'd suggest making a new PatKind
(or at least adding a distinguishing field to PatKind::Deref
so there's not a "gotcha" if you forget to check the pointer type when figuring out how to interpret a deref; my personal preference would be a new PatKind
, but I could be convinced otherwise).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now this code seems to be unreachable because &pin
becomes PatKind::RefPin
instead of PatKind::Deref
.
☔ The latest upstream changes (presumably #137940) made this pull request unmergeable. Please resolve the merge conflicts. |
Thanks @RalfJung, @dianne, @Nadrieril, and @traviscross for your helpful review! I try to summarise your suggestions before adjusting my implementation, ensuring that I don't make any mistakes or miss anything important (and pls correct me or complete me if that happens).
Footnotes
|
One note here, just to clarify in case I didn't give enough context: this is specifically referring to Either way, you'll also need to adjust
I'm not sure how the final version of this should look, but would it make sense just to have
My suggestion regarding deref patterns is that matching through pinned refs could be represented in exhaustiveness analysis with a Footnotes
|
Note that what caused us to NAK that PR is that it implemented this behavior: fn f() {
let pin mut x = ();
let _: () = x;
} More likely than not, we're not going to want that. What we do likely want, though, is: fn f() {
let ref pin mut x = ();
let _: &pin mut () = x;
} |
Some changes occurred in src/tools/rustfmt cc @rust-lang/rustfmt |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have adjusted my implementation according to my summary and @dianne's feedback, but I'm still confused about the pattern analysis part.
I got some basic knowledge from Pattern and Exhaustiveness Checking in rustc-dev-guide and Match exhaustiveness and redundancy algorithm from the in-crate docs, but I'm not sure if adding a Constructor::RefPin
and ConstructorSet::RefPin
could make sense.
I still feel that &pin
is quite similar to the &
(Ref
) in terms of PatKind
or Constructor
in my current implementation, and that is why I added a Pinnedness
field to the Constructor::Ref
in my last implementation.
The only difference I can tell is the case when Pin { .. }
and &pin
patterns appeared at the same time, like that in deref patterns (in #140106):
struct Foo<T, U>(T, U);
fn f<T, U>(foo: &pin mut Foo) {
match foo {
// This is lowered to `&pin Foo(ref pin mut x, ref pin mut y)` in THIR,
// which (I think) is consistent with match ergonomics in edition 2024.
Foo(x, y) => {},
// This directly matches the `Pin` struct, which should be reported as
// mixed usage of deref pattern ctors like in #140106.
Pin { .. } => {},
}
}
@traviscross @dianne @Nadrieril Do you have any comments or other ideas about the current implementation of the pattern analysis? (I marked this PR as draft in order not to disturb too many people, since there are still some works, like testing, to complete.)
// This is a pin ref pattern. | ||
ty::Adt(adt, args) | ||
if adt.is_pin() | ||
&& args.type_at(0).is_ref() | ||
&& self.tcx.features().pin_ergonomics() => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now this code seems to be unreachable because &pin
becomes PatKind::RefPin
instead of PatKind::Deref
.
16ddefa
to
203f504
Compare
b967f36
to
196991d
Compare
This comment has been minimized.
This comment has been minimized.
196991d
to
a3f8485
Compare
r? nadrieril perhaps |
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