Don't require unsafe for struct and array patterns against union fields - #161771
Don't require unsafe for struct and array patterns against union fields#161771Jules-Bertholet wants to merge 8 commits into
unsafe for struct and array patterns against union fields#161771Conversation
|
r? @folkertdev rustbot has assigned @folkertdev. Use Why was this reviewer chosen?The reviewer was selected based on:
|
f7e9593 to
2c56306
Compare
unsafe for PatKind::Leaf against union fieldsunsafe for struct and array patterns against union fields
|
You should actually nominate for T-lang if you want them to look at this. |
|
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`.
}
} |
|
Thanks for the correction, I edited the OP |
|
Just checking: we still 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 { .. } |
There was a problem hiding this comment.
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".
There was a problem hiding this comment.
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.
|
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? |
|
@tmandry Here you go: rust-lang/reference#2350
|
| self.inside_adt = old_inside_adt; | ||
| } | ||
| _ => { | ||
| visit::walk_pat(self, pat); |
There was a problem hiding this comment.
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.
This comment was marked as resolved.
This comment was marked as resolved.
|
@theemathas see the newly added test |
This comment was marked as resolved.
This comment was marked as resolved.
264893b to
fea33d3
Compare
|
Whoops, I forgot the important part… 🤦 fixed |
fea33d3 to
3f6a9c5
Compare
|
Some changes occurred in exhaustiveness checking cc @Nadrieril Some changes occurred in match lowering cc @Nadrieril Some changes occurred in match checking cc @Nadrieril |
These patterns don't access the union directly, only their subpatterns do. So there is no need to require `unsafe`.
75bde64 to
da31d71
Compare
|
This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
|
I've addressed the semver hazard issue as it pertains to this PR. The Reference PR has also been updated. |
da31d71 to
950d61e
Compare
| if self.in_union_destructure | ||
| && !has_rest | ||
| && (single_variant.field_list_has_applicable_non_exhaustive() | ||
| || single_variant | ||
| .fields | ||
| .iter() | ||
| .any(|f| !f.vis.is_accessible_from(scope, self.tcx))) | ||
| { | ||
| // This pattern must have been lowered from a constant. | ||
| // Changes to private implementation details of said constant | ||
| // must not affect whether we require `unsafe`. | ||
| self.requires_unsafe(pat.span, AccessToUnionField); | ||
| return; | ||
| } |
There was a problem hiding this comment.
I'm confused by the logic here. Are you using has_rest/privacy to detect constant patterns? I'd prefer to add a thir pattern node that remembers patterns that were lowered from a constant (this keeps coming up and will be added by #155216 anyway), and use that to know whether we're inside a constant.
There was a problem hiding this comment.
I'm generally confused by what has_rest has to do with this PR, given that it's equivalent to a bunch of wildcard patterns anyway.
There was a problem hiding this comment.
Are you using has_rest/privacy to detect constant patterns?
Yes, specifically constant patterns whose equivalent expanded pattern could not have been written directly at the location the pattern is being used. If a struct has non-visible fields or is foreign non_exhaustive, then you need a rest pattern to do a direct pattern match; the absence of such a pattern (constants don't have rest patterns) means the pattern was expanded from a constant in another crate.
I was going for the smallest possible change; if you have a suggestion for a cleaner way to carry though this information, that's fine, will gladly do it your way.
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:
Also removes the
unsaferequirement for the unstable guard patterns (#129967) in this position.Reference PR: rust-lang/reference#2350
@rustbot label T-lang needs-fcp A-patterns F-guard_patterns I-lang-nominated