-
-
Notifications
You must be signed in to change notification settings - Fork 14.2k
Make closure capturing have consistent and correct behaviour around patterns #138961
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
Changes from all commits
9cb47c6
e25803a
1cc7e7f
8800f95
462b51a
19f6bc4
e6c5999
3d5d1d7
a2e249c
4f7915b
d05b1d7
2443fba
ae19274
9e98885
de65837
55bbe05
011c5ed
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| #![warn(clippy::search_is_some)] | ||
| pub struct Thing; | ||
| //@no-rustfix | ||
| pub fn has_thing(things: &[Thing]) -> bool { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| // This test serves to document the change in semantics introduced by | ||
| // rust-lang/rust#138961. | ||
| // | ||
| // A corollary of partial-pattern.rs: while the tuple access testcase makes | ||
| // it clear why these semantics are useful, it is actually the dereference | ||
| // being performed by the pattern that matters. | ||
|
|
||
| fn main() { | ||
| // the inner reference is dangling | ||
| let x: &&u32 = unsafe { | ||
| let x: u32 = 42; | ||
| &&* &raw const x | ||
| }; | ||
|
|
||
| let _ = || { //~ ERROR: encountered a dangling reference | ||
| match x { | ||
| &&_y => {}, | ||
| } | ||
| }; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| error: Undefined Behavior: constructing invalid value: encountered a dangling reference (use-after-free) | ||
| --> tests/fail/closures/deref-in-pattern.rs:LL:CC | ||
| | | ||
| LL | let _ = || { | ||
| | _____________^ | ||
| LL | | match x { | ||
| LL | | &&_y => {}, | ||
| LL | | } | ||
| LL | | }; | ||
| | |_____^ Undefined Behavior occurred here | ||
| | | ||
| = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior | ||
| = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information | ||
|
|
||
| note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace | ||
|
|
||
| error: aborting due to 1 previous error | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| // This test serves to document the change in semantics introduced by | ||
| // rust-lang/rust#138961. | ||
| // | ||
| // Previously, the closure would capture the entirety of x, and access *(*x).0 | ||
| // when called. Now, the closure only captures *(*x).0, which means that | ||
| // a &*(*x).0 reborrow happens when the closure is constructed. | ||
| // | ||
| // Hence, if one of the references is dangling, this constitutes newly introduced UB | ||
| // in the case where the closure doesn't get called. This isn't a big deal, | ||
| // because while opsem only now considers this to be UB, the unsafe code | ||
| // guidelines have long recommended against any handling of dangling references. | ||
|
|
||
| fn main() { | ||
| // the inner references are dangling | ||
| let x: &(&u32, &u32) = unsafe { | ||
| let a = 21; | ||
| let b = 37; | ||
| let ra = &* &raw const a; | ||
| let rb = &* &raw const b; | ||
| &(ra, rb) | ||
| }; | ||
|
|
||
| let _ = || { //~ ERROR: encountered a dangling reference | ||
| match x { | ||
| (&_y, _) => {}, | ||
| } | ||
| }; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| error: Undefined Behavior: constructing invalid value: encountered a dangling reference (use-after-free) | ||
| --> tests/fail/closures/partial-pattern.rs:LL:CC | ||
| | | ||
| LL | let _ = || { | ||
| | _____________^ | ||
| LL | | match x { | ||
| LL | | (&_y, _) => {}, | ||
| LL | | } | ||
| LL | | }; | ||
| | |_____^ Undefined Behavior occurred here | ||
| | | ||
| = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior | ||
| = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information | ||
|
|
||
| note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace | ||
|
|
||
| error: aborting due to 1 previous error | ||
|
|
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The PR description sounds like "enums with multiple variants where only one of them is inhabited" are an especially interesting case. But that is not the case covered by this test. Is that deliberate?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is likely because I discovered this weird behavior: #138961 (comment) Somehow that particular test case doesn't seem to have made it into the repo. I suppose my thinking might've been that if the test was added, it be asserting something like "yes this is not what we want it to do, but right now it does this"... I could still add a test like that, though
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah... yeah the test that I expected doesn't actually work, we don't raise any UB.^^ And as you say that's not even about the closure, it's the same with a regular We should have an issue for that somewhere... I made one at rust-lang/miri#4778. But maybe there already is one in the rustc repo? |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| // Motivated by rust-lang/rust#138961, this shows how invalid discriminants interact with | ||
| // closure captures. | ||
| #![feature(never_type)] | ||
|
|
||
| #[repr(C)] | ||
| #[allow(dead_code)] | ||
| enum E { | ||
| V0, // discriminant: 0 | ||
| V1, // 1 | ||
| V2(!), // 2 | ||
| } | ||
|
|
||
| fn main() { | ||
| assert_eq!(std::mem::size_of::<E>(), 4); | ||
|
|
||
| let val = 2u32; | ||
| let ptr = (&raw const val).cast::<E>(); | ||
| let r = unsafe { &*ptr }; | ||
| let f = || { | ||
| // After rust-lang/rust#138961, constructing the closure performs a reborrow of r. | ||
| // Nevertheless, the discriminant is only actually inspected when the closure | ||
| // is called. | ||
| match r { //~ ERROR: read discriminant of an uninhabited enum variant | ||
| E::V0 => {} | ||
| E::V1 => {} | ||
| E::V2(_) => {} | ||
| } | ||
| }; | ||
|
|
||
| f(); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| error: Undefined Behavior: read discriminant of an uninhabited enum variant | ||
| --> tests/fail/closures/uninhabited-variant.rs:LL:CC | ||
| | | ||
| LL | match r { | ||
| | ^ Undefined Behavior occurred here | ||
| | | ||
| = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior | ||
| = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information | ||
| = note: BACKTRACE: | ||
| = note: inside closure at tests/fail/closures/uninhabited-variant.rs:LL:CC | ||
| note: inside `main` | ||
| --> tests/fail/closures/uninhabited-variant.rs:LL:CC | ||
| | | ||
| LL | f(); | ||
| | ^^^ | ||
|
|
||
| note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace | ||
|
|
||
| error: aborting due to 1 previous error | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| //@ known-bug: #119786 | ||
| //@ edition:2021 | ||
|
|
||
| fn enum_upvar() { | ||
| type T = impl Copy; | ||
| let foo: T = Some((1u32, 2u32)); | ||
| let x = move || { | ||
| match foo { | ||
| None => (), | ||
| Some(_) => (), | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| pub fn main() {} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| //@ known-bug: #119786 | ||
| //@ edition:2021 | ||
|
|
||
| fn enum_upvar() { | ||
| type T = impl Copy; | ||
| let foo: T = Some((1u32, 2u32)); | ||
| let x = move || { | ||
| match foo { | ||
| None => (), | ||
| Some((a, b)) => (), | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| pub fn main() {} |
Uh oh!
There was an error while loading. Please reload this page.
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.
huh, was this signed off by the clippy team?
cc @rust-lang/clippy
I grepped through the comments and didn't find any discussion around this
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.
It was. See #138961 (comment) and rust-lang/rust-clippy#16086.
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.
ah good, thanks!