Skip to content
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

Tracking issue: deref patterns #87121

Open
8 tasks done
nrc opened this issue Jul 14, 2021 · 66 comments
Open
8 tasks done

Tracking issue: deref patterns #87121

nrc opened this issue Jul 14, 2021 · 66 comments
Labels
A-patterns Relating to patterns and pattern matching C-tracking-issue Category: A tracking issue for an RFC or an unstable feature. F-deref_patterns `#![feature(deref_patterns)]` needs-rfc This change is large or controversial enough that it should have an RFC accepted before doing it. S-tracking-design-concerns Status: There are blocking ❌ design concerns. T-lang Relevant to the language team, which will review and decide on the PR/issue.

Comments

@nrc
Copy link
Member

nrc commented Jul 14, 2021

Tracking issue for implementing deref patterns (#[feature(deref_patterns)]).

deref patterns project group repo
lang team initiative issue

About tracking issues

Tracking issues are used to record the overall progress of implementation.
They are also used as hubs connecting to other relevant issues, e.g., bugs or open design questions.
A tracking issue is however not meant for large scale discussion, questions, or bug reports about a feature.
Instead, open a dedicated issue for the specific matter and add the relevant feature gate label.

Steps

Status

The current implementation uses a placeholder deref!(<pat>) syntax and is limited to types in the standard library. See the design proposal document for more details.

We limit to types in the standard library using the unstable trait DerefPure (#[feature(deref_pure_trait)]). It is not intended to be stabilized at this stage.

  • Supported std types: Box, Rc, Arc, Vec, String, Cow, Pin, ManuallyDrop, Ref, RefMut.
  • Unstable types that would be eligible : ThinBox, UniqueRc, LazyCell, LazyLock,

There is also a feature gate just for matching string literals on Strings, under #[feature(string_deref_patterns)].

Unresolved Questions

None at this stage

Implementation history

  1. F-deref_patterns S-waiting-on-bors T-compiler T-libs merged-by-bors
    compiler-errors
  2. F-deref_patterns S-waiting-on-author T-compiler T-rustdoc
    WaffleLapkin
  3. F-deref_patterns S-waiting-on-bors T-compiler
    compiler-errors
  4. F-deref_patterns S-waiting-on-bors T-compiler T-libs
    Nadrieril
  5. F-deref_patterns S-waiting-on-bors T-compiler T-libs T-rustdoc
    Nadrieril
  6. S-waiting-on-bors T-compiler
    matthewjasper
  7. F-deref_patterns S-waiting-on-bors T-compiler
    matthewjasper
  8. F-deref_patterns S-waiting-on-author T-libs
    compiler-errors
@nrc nrc added T-lang Relevant to the language team, which will review and decide on the PR/issue. C-tracking-issue Category: A tracking issue for an RFC or an unstable feature. A-patterns Relating to patterns and pattern matching labels Jul 14, 2021
@nrc
Copy link
Member Author

nrc commented Jul 16, 2021

@roxelo or @matthewjasper would you be able to mentor @chorman0773 on implementing the compiler parts of this please?

@roxelo
Copy link
Member

roxelo commented Jul 19, 2021

@nrc I would not consider myself an expert on the pattern matching code but I can try to help mentor...

@AlbertMarashi
Copy link

AlbertMarashi commented Feb 4, 2022

I have this code, it does not compile. I am trying to match a Box<str> inside of a pattern, but the error is:

expected str, found `&str`

but there's no good way to match Box<str> in a deeply nested pattern...

#![feature(box_patterns)]
struct Type {
    pub name: Box<str>,
    pub generics: Option<Vec<Type>>
}

fn main(){
    let kind = Type {
        name: "User".into(),
        generics: Some(Vec::new())
    };

    match kind {
        Type {
            name: box "User",
            generics
        } => {}
        _ => unimplemented!()
    }
}

@chorman0773
Copy link
Contributor

chorman0773 commented Feb 4, 2022 via email

@AlbertMarashi
Copy link

@chorman0773 anything I can do to help move this forward? I'd really love to see this feature in rust, it would be insanely useful for deep matching of things like ASTs.

@rdrpenguin04
Copy link

Just bonked my head into this again; how can I help?

@joshtriplett joshtriplett added S-tracking-design-concerns Status: There are blocking ❌ design concerns. needs-rfc This change is large or controversial enough that it should have an RFC accepted before doing it. labels Jul 27, 2022
@safinaskar
Copy link
Contributor

@rdrpenguin04 and everybody else who cannot wait: I made a proc macro crate, which implements deref patterns in stable Rust: https://crates.io/crates/match_deref

Manishearth added a commit to Manishearth/rust that referenced this issue Nov 16, 2022
…s, r=compiler-errors

Minimal implementation of implicit deref patterns for Strings

cc `@compiler-errors` `@BoxyUwU` rust-lang/lang-team#88 rust-lang#87121

~~I forgot to add a feature gate, will do so in a minute~~ Done
bors added a commit to rust-lang-ci/rust that referenced this issue Nov 20, 2022
… r=compiler-errors

Minimal implementation of implicit deref patterns for Strings

cc `@compiler-errors` `@BoxyUwU` rust-lang/lang-team#88 rust-lang#87121

~~I forgot to add a feature gate, will do so in a minute~~ Done
flip1995 pushed a commit to flip1995/rust that referenced this issue Nov 21, 2022
… r=compiler-errors

Minimal implementation of implicit deref patterns for Strings

cc `@compiler-errors` `@BoxyUwU` rust-lang/lang-team#88 rust-lang#87121

~~I forgot to add a feature gate, will do so in a minute~~ Done
Aaron1011 pushed a commit to Aaron1011/rust that referenced this issue Jan 6, 2023
… r=compiler-errors

Minimal implementation of implicit deref patterns for Strings

cc `@compiler-errors` `@BoxyUwU` rust-lang/lang-team#88 rust-lang#87121

~~I forgot to add a feature gate, will do so in a minute~~ Done
@tgross35
Copy link
Contributor

tgross35 commented Jan 22, 2023

Is anybody involved here able to provide a rough outline or example of what this will look like when stable? I'm imagining this will be something that's automatic for anything that implements Deref

if let Some(u8) = Some(Rc::new(10)) { ... }
if let Some("yellow world") = Some(Box::new(String::from("hello world"))) { ... }

edit: according to Mario on Zulip, this would be keywordless, maybe with a & or a * needed - that's not yet decided. Super elegant!

@simensgreen
Copy link

simensgreen commented Feb 2, 2023

Why can't we make

trait Match {
    type Target: ?Sized;
    fn do_match(&self) -> &Self::Target;
}
trait MatchMut: Match {
    fn do_match_mut(&mut self) -> &mut Self::Target;
}

how is it done with Deref and Derefmut, respectively?
The compiler somehow correlates

*x
// and
<&XType as Deref>::deref(&x)

why not do the same with match?

match x {}
// and
<XType as Match>::do_match(&x)

This would solve how to match strings, SmartPointers, and other more complex constructs without adding new syntax to the language.

@tgross35
Copy link
Contributor

tgross35 commented Feb 3, 2023

@simensgreen the trait isn't really an issue at this point, since Deref/DerefMut already gives the information needed (possibly with the marker trait DerefPure that has been discussed a few places). I think at this point, the goal is to start out supporting only builtin types (&str/String, &T/Vec, &T/Box, Rc, Arc).

And I think the blocker at this point is just time/people. Look at #98914 needed just to support strings, it's a pretty big change

@tgross35
Copy link
Contributor

tgross35 commented Feb 3, 2023

@nrc would you maybe want to update this tracking issue to point to relevant per-type implementations? Probably just:

  • String -> &str: string_deref_patterns implementation Minimal implementation of implicit deref patterns for Strings #98914 (stabilization not started)
  • Box<T> -> &T: box_deref_patterns (not started)
  • Rc<T> -> &T, Arc<T> -> &T: rc_deref_patterns (not started)
  • Vec<T> -> &[T]: slice_deref_patterns (not started)
  • impl DerefPure<Target = U> -> &U: (further discussion needed)

Assuming the goal is to chunk this huge project up into bits that could be stabilized individually.

@simensgreen
Copy link

@tgross35 The existence of such a trait will help to implement match not only for standard types, including strings, but also for any arbitrary types, like Mutex. Deref does not have enough information to make a match

@tgross35
Copy link
Contributor

tgross35 commented Feb 7, 2023

You wouldn't be able to pattern match directly on a Mutex, and that's a good thing because implicitly locking would lead to some very difficult to follow code. However, you could lock it yourself and then pattern match on the MutexGuard, which does Deref to &T.

Also, to quote zulip

doing it in general faces some thorny issues around speccing what happens when the deref has side effects, since we want to be able to optimize matches in ways that may not preserve how many deref calls are made

This would be the reason for DerefPure that's come up a few times, an (unsafe?) marker trait to specify a special case of Deref that has no side effects (which is already the case for much of the Deref use in std, there's just no way to express that)

@RalfJung
Copy link
Member

This would be the reason for DerefPure that's come up a few times, an (unsafe?) marker trait to specify a special case of Deref that has no side effects (which is already the case for much of the Deref use in std, there's just no way to express that)

How exactly would this interact with our usual safety requirements / UB? Saying "it is UB to impl DerefPure on a non-pure impl Deref" is quite problematic as this would be UB which cannot be checked by a sanitizer such as Miri. Ideally we'd end up in a situation where a wrong DerefPure can lead to surprising behavior, but is not in and of itself UB.

Cc @rust-lang/opsem

@CAD97
Copy link
Contributor

CAD97 commented Feb 28, 2023

I would initially expect it to behave like any other unsafe trait implementation: it gives downstream code permission to rely on the guaranteed properties, but doesn't result in UB until downstream code relies on that property to maintain soundness. Or IOW, library UB.

I think it probably does need to be unsafe, though, because it's not just about collapsing multiple calls into one, but also introducing calls where they wouldn't otherwise exist by strictly inorder trial evaluation of match arms. Since this makes otherwise dead code live, that feels unsafe, but it also can be justified as just safe misbehavior.

"I don't say how many times this gets executed" is a pretty textbook example of library instability which can't be validated operationally, although in this case it's as part of the language rather than library. The only guarantee I think the deref impl gets is that it's evaluated at least once if an arm containing it is

I also think there might be an element of one deref place evaluation being sufficient for both by-ref and by-ref mut binding modes, which while not technically unsafe (it's safe to do &*&mut *it to use DerefMut to get &T) also feels like it should be an unsafe guarantee that both are equivalent and interchangable places modulo provenance.

What sanitizers could do is always execute all structurally reachable pattern derefs from any arms, whether that arm is even reachable or not. Randomized sanitizers could also probabilistically vary how many times the derefs get evaluated, including zero times if unnecessary, and many more times than present in patterns.

Alternatively, the strong position is to go super strict about pattern derefs being not just pure but also just being composed from place computation primitives; in this case such implementations could even be verified statically to be only composed of such operations. Even if this isn't stated required at a language level, validating such composition dynamically ensures actual purity and as such skipping dynamic validation of spurious (non)evaluation.

Then, only partially unrelated, is the question on whether DerefPure's purity promise is utilized anywhere outside of deref patterns. E.g. it could potentially be used to enable borrow splitting, either by caching the place evaluation and/or not invalidating previously split borrows, although the latter means such place evaluation can't use typical reborrowing references.

Properly built-in place evaluation has a number of extra differences to overloadable place evaluation (pattern transparency, borrow splitting, "deref move", purity, const), and it needs to be decided how much or how little of that DerefPure should provide before it can properly be discussed whether that requires it to be unsafe.

@ThePuzzlemaker
Copy link
Contributor

ThePuzzlemaker commented Dec 29, 2023

@tgross35 I am interested in working on this. Where in the code is this lowering done, and where should I start?

@tgross35
Copy link
Contributor

@ThePuzzlemaker awesome! @Nadrieril or @CAD97 would be able to give you more details about where exactly to look, but it will touch a lot of the areas that were relevant in making this work for String #98914 (actually a lot fewer areas than that since we don’t want to do anything type-specific)

You should also drop by Zulip to coordinate efforts and ask any smaller questions, the relevant stream is here: https://rust-lang.zulipchat.com/#narrow/stream/281601-project-deref-patterns.

A reasonable first step is to make any pattern work for a single match for anything that is Deref, I don’t think there is any need to implement multiple matches (as discussed above) as part of the first PR (unless it comes naturally with the implementation).

@ThePuzzlemaker
Copy link
Contributor

ThePuzzlemaker commented Dec 29, 2023

I've realized that I think this is a little out of my ability at the moment 😅, sorry
I realized that this requires a lot more knowledge of the compiler internals than I have right now and it would take me a looong time to get that knowledge, tbh

@Nadrieril
Copy link
Member

Dw, a bunch of us are eager to get this and are slowly making progress

@cloudhead
Copy link

cloudhead commented Jan 17, 2024

Would this allow for moving out of boxes? Or only getting a reference of the inner value? It's quite common for interpreters to have a recursive type like

enum Expr {
   If(Box<Expr>, Box<Expr>, Box<Expr>),
   ...
}

And being able to destructure that by moving out of Box would make code much simpler, eg.

match expr {
  If(cond, left, right) => { /* Use owned `Expr` values here */ }
}

@Nadrieril
Copy link
Member

I think we want to get something like that eventually, but that's not included in this proposal. The difficulty is from a language design pov: what syntax to use, how/whether to generalize that to other types than Box (which draws in the thorny DerefMove situation).

@clarfonthey
Copy link
Contributor

I mean, right now as it stands, Box already has its own DerefMove sugar (specifically, *b), so, I can't imagine it'd be that bad to add custom support for moving out of deref before a fully generic solution exists.

@RalfJung
Copy link
Member

If this does not allow moving out of boxes, we should be careful to be forward-compatible with eventually allowing moving out of boxes.

@Nadrieril
Copy link
Member

Work on the feature is restarting. I'd like to remind everyone that

A tracking issue is however not meant for large scale discussion, questions, or bug reports about a feature.
Instead, open a dedicated issue for the specific matter and add the relevant feature gate label.

especially since the design aspects are contentious. Thank you! See you on zulip for discussions.

@Nadrieril
Copy link
Member

Dear T-lang, the liaison for this initiative (cramertj) has retired from the lang team. Progress is therefore halted until a new liaison is found. I would like to raise this for the next triage meeting so a liaison can be found, if the team still wants this feature to happen. I will be there to answer questions.

@Nadrieril Nadrieril added the I-lang-nominated The issue / PR has been nominated for discussion during a lang team meeting. label Mar 19, 2024
@traviscross
Copy link
Contributor

@rustbot labels -I-lang-nominated

We discussed this in the lang triage call today. The consensus was that I would pick up the liaisoning here, so please let me know whenever anything is needed to move this along. Thanks to @Nadrieril for pushing this forward; we'll all excited to see the outcome of that.

@rustbot rustbot removed the I-lang-nominated The issue / PR has been nominated for discussion during a lang team meeting. label Mar 20, 2024
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Mar 21, 2024
…r=compiler-errors

deref patterns: bare-bones feature gate and typechecking

I am restarting the deref patterns experimentation. This introduces a feature gate under the lang-team [experimental feature](https://github.com/rust-lang/lang-team/blob/master/src/how_to/experiment.md) process, with [`@cramertj` as lang-team liaison](rust-lang/lang-team#88) (it's been a while though, you still ok with this `@cramertj?).` Tracking issue: rust-lang#87121.

This is the barest-bones implementation I could think of:
- explicit syntax, reusing `box <pat>` because that saves me a ton of work;
- use `Deref` as a marker trait (instead of a yet-to-design `DerefPure`);
- no support for mutable patterns with `DerefMut` for now;
- MIR lowering will come in the next PR. It's the trickiest part.

My goal is to let us figure out the MIR lowering part, which might take some work. And hopefully get something working for std types soon.

This is in large part salvaged from `@fee1-dead's` rust-lang#119467.

r? `@compiler-errors`
workingjubilee added a commit to workingjubilee/rustc that referenced this issue Mar 21, 2024
…r=compiler-errors

deref patterns: bare-bones feature gate and typechecking

I am restarting the deref patterns experimentation. This introduces a feature gate under the lang-team [experimental feature](https://github.com/rust-lang/lang-team/blob/master/src/how_to/experiment.md) process, with [``@cramertj`` as lang-team liaison](rust-lang/lang-team#88) (it's been a while though, you still ok with this ``@cramertj?).`` Tracking issue: rust-lang#87121.

This is the barest-bones implementation I could think of:
- explicit syntax, reusing `box <pat>` because that saves me a ton of work;
- use `Deref` as a marker trait (instead of a yet-to-design `DerefPure`);
- no support for mutable patterns with `DerefMut` for now;
- MIR lowering will come in the next PR. It's the trickiest part.

My goal is to let us figure out the MIR lowering part, which might take some work. And hopefully get something working for std types soon.

This is in large part salvaged from ``@fee1-dead's`` rust-lang#119467.

r? ``@compiler-errors``
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Mar 21, 2024
…r=compiler-errors

deref patterns: bare-bones feature gate and typechecking

I am restarting the deref patterns experimentation. This introduces a feature gate under the lang-team [experimental feature](https://github.com/rust-lang/lang-team/blob/master/src/how_to/experiment.md) process, with [```@cramertj``` as lang-team liaison](rust-lang/lang-team#88) (it's been a while though, you still ok with this ```@cramertj?).``` Tracking issue: rust-lang#87121.

This is the barest-bones implementation I could think of:
- explicit syntax, reusing `box <pat>` because that saves me a ton of work;
- use `Deref` as a marker trait (instead of a yet-to-design `DerefPure`);
- no support for mutable patterns with `DerefMut` for now;
- MIR lowering will come in the next PR. It's the trickiest part.

My goal is to let us figure out the MIR lowering part, which might take some work. And hopefully get something working for std types soon.

This is in large part salvaged from ```@fee1-dead's``` rust-lang#119467.

r? ```@compiler-errors```
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Mar 21, 2024
…r=compiler-errors

deref patterns: bare-bones feature gate and typechecking

I am restarting the deref patterns experimentation. This introduces a feature gate under the lang-team [experimental feature](https://github.com/rust-lang/lang-team/blob/master/src/how_to/experiment.md) process, with [````@cramertj```` as lang-team liaison](rust-lang/lang-team#88) (it's been a while though, you still ok with this ````@cramertj?).```` Tracking issue: rust-lang#87121.

This is the barest-bones implementation I could think of:
- explicit syntax, reusing `box <pat>` because that saves me a ton of work;
- use `Deref` as a marker trait (instead of a yet-to-design `DerefPure`);
- no support for mutable patterns with `DerefMut` for now;
- MIR lowering will come in the next PR. It's the trickiest part.

My goal is to let us figure out the MIR lowering part, which might take some work. And hopefully get something working for std types soon.

This is in large part salvaged from ````@fee1-dead's```` rust-lang#119467.

r? ````@compiler-errors````
rust-timer added a commit to rust-lang-ci/rust that referenced this issue Mar 21, 2024
Rollup merge of rust-lang#122222 - Nadrieril:deref-pat-feature-gate, r=compiler-errors

deref patterns: bare-bones feature gate and typechecking

I am restarting the deref patterns experimentation. This introduces a feature gate under the lang-team [experimental feature](https://github.com/rust-lang/lang-team/blob/master/src/how_to/experiment.md) process, with [````@cramertj```` as lang-team liaison](rust-lang/lang-team#88) (it's been a while though, you still ok with this ````@cramertj?).```` Tracking issue: rust-lang#87121.

This is the barest-bones implementation I could think of:
- explicit syntax, reusing `box <pat>` because that saves me a ton of work;
- use `Deref` as a marker trait (instead of a yet-to-design `DerefPure`);
- no support for mutable patterns with `DerefMut` for now;
- MIR lowering will come in the next PR. It's the trickiest part.

My goal is to let us figure out the MIR lowering part, which might take some work. And hopefully get something working for std types soon.

This is in large part salvaged from ````@fee1-dead's```` rust-lang#119467.

r? ````@compiler-errors````
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue May 3, 2024
…ompiler-errors

deref patterns: impl `DerefPure` for more std types

Context: [deref patterns](rust-lang#87121). The requirements of `DerefPure` aren't precise yet, but these types unambiguously satisfy them.

Interestingly, a hypothetical `impl DerefMut for Cow` that does a `Clone` would *not* be eligible for `DerefPure` if we allow mixing deref patterns with normal patterns. If the following is exhaustive then the `DerefMut` would cause UB:
```rust
match &mut Cow::Borrowed(&()) {
    Cow::Owned(_) => ..., // Doesn't match
    deref!(_x) if false => ..., // Causes the variant to switch to `Owned`
    Cow::Borrowed(_) => ..., // Doesn't match
    // We reach unreachable
}
```
rust-timer added a commit to rust-lang-ci/rust that referenced this issue May 3, 2024
Rollup merge of rust-lang#123480 - Nadrieril:impl-all-derefpures, r=compiler-errors

deref patterns: impl `DerefPure` for more std types

Context: [deref patterns](rust-lang#87121). The requirements of `DerefPure` aren't precise yet, but these types unambiguously satisfy them.

Interestingly, a hypothetical `impl DerefMut for Cow` that does a `Clone` would *not* be eligible for `DerefPure` if we allow mixing deref patterns with normal patterns. If the following is exhaustive then the `DerefMut` would cause UB:
```rust
match &mut Cow::Borrowed(&()) {
    Cow::Owned(_) => ..., // Doesn't match
    deref!(_x) if false => ..., // Causes the variant to switch to `Owned`
    Cow::Borrowed(_) => ..., // Doesn't match
    // We reach unreachable
}
```
@AlbertMarashi
Copy link

AlbertMarashi commented Aug 2, 2024

#![feature(string_deref_patterns)] doesn't seem to work with rust-analyzer. Any workarounds?

@Nadrieril
Copy link
Member

This is still pretty experimental. To make it work, rust-analyzer would have to update their type-checker to recognize these new patterns. It feels premature at this point of the development of the feature.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-patterns Relating to patterns and pattern matching C-tracking-issue Category: A tracking issue for an RFC or an unstable feature. F-deref_patterns `#![feature(deref_patterns)]` needs-rfc This change is large or controversial enough that it should have an RFC accepted before doing it. S-tracking-design-concerns Status: There are blocking ❌ design concerns. T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests