Skip to content

Don't require unsafe for struct and array patterns against union fields - #161771

Open
Jules-Bertholet wants to merge 6 commits into
rust-lang:mainfrom
Jules-Bertholet:safe-union-leaf-patterns
Open

Don't require unsafe for struct and array patterns against union fields#161771
Jules-Bertholet wants to merge 6 commits into
rust-lang:mainfrom
Jules-Bertholet:safe-union-leaf-patterns

Conversation

@Jules-Bertholet

@Jules-Bertholet Jules-Bertholet commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

These patterns don't access the union directly, only their subpatterns do. So there is no need to require unsafe.

For example, the following now compiles:

union Foo {
    field: [u8; 3],
}

fn bar(foo: Foo) {
    match foo {
        Foo { field: [_, _, _], } => (),
    }
}

Also removes the unsafe requirement for the unstable guard patterns (#129967) in this position.

@rustbot label T-lang needs-fcp A-patterns F-guard_patterns I-lang-nominated

@rustbot rustbot added 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 Aug 25, 2026
@rustbot

rustbot commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator

r? @folkertdev

rustbot has assigned @folkertdev.
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

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: compiler
  • compiler expanded to 75 candidates
  • Random selection from 16 candidates

@rustbot rustbot added A-patterns Relating to patterns and pattern matching needs-fcp This change is insta-stable, or significant enough to need a team FCP to proceed. T-lang Relevant to the language team labels Aug 25, 2026
These patterns don't access the union directly,
only their subpatterns do. So there is no need to require `unsafe`.
@Jules-Bertholet
Jules-Bertholet force-pushed the safe-union-leaf-patterns branch from f7e9593 to 2c56306 Compare August 25, 2026 15:43
@Jules-Bertholet Jules-Bertholet changed the title Don't require unsafe for PatKind::Leaf against union fields Don't require unsafe for struct and array patterns against union fields Aug 25, 2026
@rustbot rustbot added the F-guard_patterns `#![feature(guard_patterns)]` label Aug 25, 2026
@folkertdev

Copy link
Copy Markdown
Contributor

You should actually nominate for T-lang if you want them to look at this.

@rustbot rustbot added the I-lang-nominated Nominated for discussion during a lang team meeting. label Aug 25, 2026
@folkertdev folkertdev added S-waiting-on-t-lang Status: Awaiting decision from T-lang and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Aug 25, 2026
@traviscross traviscross added P-lang-drag-1 Lang team prioritization drag level 1. https://rust-lang.zulipchat.com/#narrow/channel/410516-t-lang I-lang-radar Items that are on lang's radar and will need eventual work or consideration. labels Aug 26, 2026
@traviscross

Copy link
Copy Markdown
Contributor

Worth noting, for our discussion, that the example in the PR description compiles today, i.e., ahead of this PR landing:

union Foo {
    field: u8,
}

fn bar(foo: Foo) {
    match foo {
        Foo { field: _ } => (), // OK!
    }
}

What fails are things like this:

union Foo {
    field: (u8,),
}

fn bar(foo: Foo) {
    match foo {
        Foo { field: (..) } => (), // ERROR: Needs `unsafe`.
    }
}

@Jules-Bertholet

Copy link
Copy Markdown
Contributor Author

Thanks for the correction, I edited the OP

@scottmcm

scottmcm commented Sep 2, 2026

Copy link
Copy Markdown
Member

Just checking: we still FakeRead/PlaceReference (I don't remember which it should be) the union when doing this, right?

Assuming we do, then I agree that allowing these categories of irrefutable patterns in union patterns makes sense 👍

| PatKind::Deref { .. }
| PatKind::DerefPattern { .. }
| PatKind::Range { .. }
| PatKind::Slice { .. }

@scottmcm scottmcm Sep 2, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pondering: [..] is also an irrefutable pattern but isn't updated here (right?)

I don't know if it's possible, but could this whole match change to being about irrefutable pattern instead, or something? If we have to whack-a-mole a whole bunch of things here, that makes me less "oh yeah let's do it" than I was before, since I don't know why people would write this.

(Notably if you're using a pat_param from a macro it'd actually be easier for it to always be unsafe so you don't need to suppress the unneeded-unsafe if they pass something simple.)

Part of why we said that unsafeck is on THIR is that it's more of a lexical check than a flow-sensitive one, so being a bit more unsafe than strictly necessary is generally fine if it's something that the human description of the thing is something that people would say "it's unsafe to do that".

View changes since the review

@Jules-Bertholet Jules-Bertholet Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pondering: [..] is also an irrefutable pattern but isn't updated here (right?)

Yes it is. There's even a test.

could this whole match change to being about irrefutable pattern instead

No, irrefutability isn't sufficient. x is an irrefutable pattern but still needs to be unsafe; & _ probably should be as well. Nor is it even necessary; the unstable guard patterns are refutable, but shouldn't require unsafe.

What we care about is that the pattern does not perform a read/assert validity.

@tmandry

tmandry commented Sep 2, 2026

Copy link
Copy Markdown
Member

We talked about this in today's lang meeting. This change seems fine individually, but along the lines of @scottmcm's comments above, I'd like to do it in a way that preserves (or improves) the overall consistency and simplicity of the language.

One way to evaluate that would be to review a reference PR for the change. @Jules-Bertholet would you be willing to draft one for us to review?

@Jules-Bertholet

Copy link
Copy Markdown
Contributor Author

@tmandry Here you go: rust-lang/reference#2350

While drafting, I noticed one issue: this change makes #162213 slightly worse, because now whether a const pattern requires unsafe can depend on what should be private implementation details of the const item. (I don't think that issue should block this change, as the semver hazard exists either way.)

self.inside_adt = old_inside_adt;
}
_ => {
visit::walk_pat(self, pat);

@theemathas theemathas Sep 5, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't correctly handle guard patterns. Currently, it flags any patterns inside a guard pattern condition inside a union pattern as also accessing the union. This is incorrect. For example, the following code doesn't compile, even with this PR, but I think it should:

#![feature(guard_patterns)]
#![expect(incomplete_features)]

union MyUnion {
    thing: i32,
}

fn foo(x: MyUnion) {
    match x {
        MyUnion {
            thing: (_ if matches!(1, 1)),
        } => {}
    }
}

The in_union_destructure flag needs to reset to false when walking the guard pattern condition.

View changes since the review

@theemathas

Copy link
Copy Markdown
Contributor

whether a const pattern requires unsafe can depend on what should be private implementation details of the const item.

@Jules-Bertholet What do you mean here? From my testing, it seems that the potential semver hazard is just that an "implicit const" defined by a unit struct can be safely matched in a union with this PR, while an actual const item cannot.

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 F-guard_patterns `#![feature(guard_patterns)]` I-lang-nominated Nominated for discussion during a lang team meeting. I-lang-radar Items that are on lang's radar and will need eventual work or consideration. needs-fcp This change is insta-stable, or significant enough to need a team FCP to proceed. P-lang-drag-1 Lang team prioritization drag level 1. https://rust-lang.zulipchat.com/#narrow/channel/410516-t-lang S-waiting-on-t-lang Status: Awaiting decision from T-lang T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants