Skip to content

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

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
271 changes: 113 additions & 158 deletions compiler/rustc_hir_typeck/src/expr_use_visitor.rs

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions compiler/rustc_hir_typeck/src/upvar.rs
Original file line number Diff line number Diff line change
@@ -761,6 +761,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// ],
/// }
/// ```
#[instrument(level = "debug", skip(self))]
fn compute_min_captures(
&self,
closure_def_id: LocalDefId,
@@ -2030,6 +2031,7 @@ struct InferBorrowKind<'tcx> {
}

impl<'tcx> euv::Delegate<'tcx> for InferBorrowKind<'tcx> {
#[instrument(skip(self), level = "debug")]
fn fake_read(
&mut self,
place_with_id: &PlaceWithHirId<'tcx>,
@@ -2120,6 +2122,7 @@ impl<'tcx> euv::Delegate<'tcx> for InferBorrowKind<'tcx> {
}

/// Rust doesn't permit moving fields out of a type that implements drop
#[instrument(skip(fcx), ret, level = "debug")]
fn restrict_precision_for_drop_types<'a, 'tcx>(
fcx: &'a FnCtxt<'a, 'tcx>,
mut place: Place<'tcx>,
@@ -2179,6 +2182,7 @@ fn restrict_precision_for_unsafe(
/// - No Index projections are captured, since arrays are captured completely.
/// - No unsafe block is required to capture `place`
/// Returns the truncated place and updated capture mode.
#[instrument(ret, level = "debug")]
fn restrict_capture_precision(
place: Place<'_>,
curr_mode: ty::UpvarCapture,
@@ -2208,6 +2212,7 @@ fn restrict_capture_precision(
}

/// Truncate deref of any reference.
#[instrument(ret, level = "debug")]
fn adjust_for_move_closure(
mut place: Place<'_>,
mut kind: ty::UpvarCapture,
@@ -2222,6 +2227,7 @@ fn adjust_for_move_closure(
}

/// Truncate deref of any reference.
#[instrument(ret, level = "debug")]
fn adjust_for_use_closure(
mut place: Place<'_>,
mut kind: ty::UpvarCapture,
@@ -2237,6 +2243,7 @@ fn adjust_for_use_closure(

/// Adjust closure capture just that if taking ownership of data, only move data
/// from enclosing stack frame.
#[instrument(ret, level = "debug")]
fn adjust_for_non_move_closure(
mut place: Place<'_>,
mut kind: ty::UpvarCapture,
@@ -2561,6 +2568,7 @@ fn determine_place_ancestry_relation<'tcx>(
/// // it is constrained to `'a`
/// }
/// ```
#[instrument(ret, level = "debug")]
fn truncate_capture_for_optimization(
mut place: Place<'_>,
mut curr_mode: ty::UpvarCapture,
6 changes: 6 additions & 0 deletions compiler/rustc_mir_build/src/builder/matches/match_pair.rs
Original file line number Diff line number Diff line change
@@ -300,6 +300,12 @@ impl<'tcx> MatchPairTree<'tcx> {

if let Some(test_case) = test_case {
// This pattern is refutable, so push a new match-pair node.
//
// Note: unless test_case is TestCase::Or, place must not be None.
// This means that the closure capture analysis in
// rustc_hir_typeck::upvar, and in particular the pattern handling
// code of ExprUseVisitor, must capture all of the places we'll use.
// Make sure to keep these two parts in sync!
match_pairs.push(MatchPairTree {
place,
test_case,
20 changes: 20 additions & 0 deletions src/tools/miri/tests/fail/closures/deref-in-pattern.rs
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 => {},
}
};
}
20 changes: 20 additions & 0 deletions src/tools/miri/tests/fail/closures/deref-in-pattern.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
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 | | };
| |_____^ constructing invalid value: encountered a dangling reference (use-after-free)
|
= 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 `main` at tests/fail/closures/deref-in-pattern.rs:LL:CC

note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

error: aborting due to 1 previous error

28 changes: 28 additions & 0 deletions src/tools/miri/tests/fail/closures/partial-pattern.rs
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, _) => {},
}
};
}
20 changes: 20 additions & 0 deletions src/tools/miri/tests/fail/closures/partial-pattern.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
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 | | };
| |_____^ constructing invalid value: encountered a dangling reference (use-after-free)
|
= 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 `main` at tests/fail/closures/partial-pattern.rs:LL:CC

note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

error: aborting due to 1 previous error

31 changes: 31 additions & 0 deletions src/tools/miri/tests/fail/closures/uninhabited-variant.rs
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();
}
20 changes: 20 additions & 0 deletions src/tools/miri/tests/fail/closures/uninhabited-variant.stderr
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 {
| ^ read discriminant of an uninhabited enum variant
|
= 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

File renamed without changes.
15 changes: 15 additions & 0 deletions tests/crashes/119786-2.rs
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() {}
15 changes: 15 additions & 0 deletions tests/crashes/119786-3.rs
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() {}
17 changes: 0 additions & 17 deletions tests/crashes/137467-1.rs

This file was deleted.

18 changes: 0 additions & 18 deletions tests/crashes/137467-2.rs

This file was deleted.

8 changes: 0 additions & 8 deletions tests/crashes/137467-3.rs

This file was deleted.

11 changes: 0 additions & 11 deletions tests/crashes/140011.rs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -4,18 +4,16 @@ fn foo::{closure#0}::{closure#0}(_1: {async closure body@$DIR/async_closure_fake
yields ()
{
debug _task_context => _2;
debug f => (*(_1.0: &&Foo));
debug f => (*(_1.0: &Foo));
let mut _0: ();
let mut _3: &Foo;
let mut _4: &&Foo;
let mut _5: &&&Foo;
let mut _6: isize;
let mut _7: bool;
let mut _5: isize;
let mut _6: bool;

bb0: {
PlaceMention((*(_1.0: &&Foo)));
_6 = discriminant((*(*(_1.0: &&Foo))));
switchInt(move _6) -> [0: bb2, otherwise: bb1];
_5 = discriminant((*(_1.0: &Foo)));
switchInt(move _5) -> [0: bb2, otherwise: bb1];
}

bb1: {
@@ -32,28 +30,25 @@ yields ()
}

bb4: {
FakeRead(ForMatchedPlace(None), (*(_1.0: &&Foo)));
unreachable;
}

bb5: {
_3 = &fake shallow (*(*(_1.0: &&Foo)));
_4 = &fake shallow (*(_1.0: &&Foo));
_5 = &fake shallow (_1.0: &&Foo);
StorageLive(_7);
_7 = const true;
switchInt(move _7) -> [0: bb8, otherwise: bb7];
_3 = &fake shallow (*(_1.0: &Foo));
_4 = &fake shallow (_1.0: &Foo);
StorageLive(_6);
_6 = const true;
switchInt(move _6) -> [0: bb8, otherwise: bb7];
}

bb6: {
falseEdge -> [real: bb3, imaginary: bb1];
}

bb7: {
StorageDead(_7);
StorageDead(_6);
FakeRead(ForMatchGuard, _3);
FakeRead(ForMatchGuard, _4);
FakeRead(ForMatchGuard, _5);
_0 = const ();
goto -> bb10;
}
@@ -63,7 +58,7 @@ yields ()
}

bb9: {
StorageDead(_7);
StorageDead(_6);
goto -> bb6;
}

Original file line number Diff line number Diff line change
@@ -4,18 +4,16 @@ fn foo::{closure#0}::{synthetic#0}(_1: {async closure body@$DIR/async_closure_fa
yields ()
{
debug _task_context => _2;
debug f => (_1.0: &Foo);
debug f => (*(_1.0: &Foo));
let mut _0: ();
let mut _3: &Foo;
let mut _4: &&Foo;
let mut _5: &&&Foo;
let mut _6: isize;
let mut _7: bool;
let mut _5: isize;
let mut _6: bool;

bb0: {
PlaceMention((_1.0: &Foo));
_6 = discriminant((*(_1.0: &Foo)));
switchInt(move _6) -> [0: bb2, otherwise: bb1];
_5 = discriminant((*(_1.0: &Foo)));
switchInt(move _5) -> [0: bb2, otherwise: bb1];
}

bb1: {
@@ -29,24 +27,22 @@ yields ()

bb3: {
_3 = &fake shallow (*(_1.0: &Foo));
_4 = &fake shallow (_1.0: &Foo);
nop;
StorageLive(_7);
_7 = const true;
switchInt(move _7) -> [0: bb5, otherwise: bb4];
StorageLive(_6);
_6 = const true;
switchInt(move _6) -> [0: bb5, otherwise: bb4];
}

bb4: {
StorageDead(_7);
StorageDead(_6);
FakeRead(ForMatchGuard, _3);
FakeRead(ForMatchGuard, _4);
FakeRead(ForMatchGuard, _5);
_0 = const ();
goto -> bb6;
}

bb5: {
StorageDead(_7);
StorageDead(_6);
falseEdge -> [real: bb1, imaginary: bb1];
}

2 changes: 2 additions & 0 deletions tests/ui/closures/2229_closure_analysis/capture-enums.rs
Original file line number Diff line number Diff line change
@@ -22,13 +22,15 @@ fn multi_variant_enum() {
//~| ERROR Min Capture analysis includes:
if let Info::Point(_, _, str) = point {
//~^ NOTE: Capturing point[] -> Immutable
//~| NOTE: Capturing point[] -> Immutable
//~| NOTE: Capturing point[(2, 0)] -> ByValue
//~| NOTE: Min Capture point[] -> ByValue
println!("{}", str);
}

if let Info::Meta(_, v) = meta {
//~^ NOTE: Capturing meta[] -> Immutable
//~| NOTE: Capturing meta[] -> Immutable
//~| NOTE: Capturing meta[(1, 1)] -> ByValue
//~| NOTE: Min Capture meta[] -> ByValue
println!("{:?}", v);
26 changes: 18 additions & 8 deletions tests/ui/closures/2229_closure_analysis/capture-enums.stderr
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@ LL | let c = #[rustc_capture_analysis]
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date

error[E0658]: attributes on expressions are experimental
--> $DIR/capture-enums.rs:48:13
--> $DIR/capture-enums.rs:50:13
|
LL | let c = #[rustc_capture_analysis]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -34,18 +34,28 @@ note: Capturing point[] -> Immutable
|
LL | if let Info::Point(_, _, str) = point {
| ^^^^^
note: Capturing point[] -> Immutable
--> $DIR/capture-enums.rs:23:41
|
LL | if let Info::Point(_, _, str) = point {
| ^^^^^
note: Capturing point[(2, 0)] -> ByValue
--> $DIR/capture-enums.rs:23:41
|
LL | if let Info::Point(_, _, str) = point {
| ^^^^^
note: Capturing meta[] -> Immutable
--> $DIR/capture-enums.rs:30:35
--> $DIR/capture-enums.rs:31:35
|
LL | if let Info::Meta(_, v) = meta {
| ^^^^
note: Capturing meta[] -> Immutable
--> $DIR/capture-enums.rs:31:35
|
LL | if let Info::Meta(_, v) = meta {
| ^^^^
note: Capturing meta[(1, 1)] -> ByValue
--> $DIR/capture-enums.rs:30:35
--> $DIR/capture-enums.rs:31:35
|
LL | if let Info::Meta(_, v) = meta {
| ^^^^
@@ -67,13 +77,13 @@ note: Min Capture point[] -> ByValue
LL | if let Info::Point(_, _, str) = point {
| ^^^^^
note: Min Capture meta[] -> ByValue
--> $DIR/capture-enums.rs:30:35
--> $DIR/capture-enums.rs:31:35
|
LL | if let Info::Meta(_, v) = meta {
| ^^^^

error: First Pass analysis includes:
--> $DIR/capture-enums.rs:52:5
--> $DIR/capture-enums.rs:54:5
|
LL | / || {
LL | |
@@ -85,13 +95,13 @@ LL | | };
| |_____^
|
note: Capturing point[(2, 0)] -> ByValue
--> $DIR/capture-enums.rs:55:47
--> $DIR/capture-enums.rs:57:47
|
LL | let SingleVariant::Point(_, _, str) = point;
| ^^^^^

error: Min Capture analysis includes:
--> $DIR/capture-enums.rs:52:5
--> $DIR/capture-enums.rs:54:5
|
LL | / || {
LL | |
@@ -103,7 +113,7 @@ LL | | };
| |_____^
|
note: Min Capture point[(2, 0)] -> ByValue
--> $DIR/capture-enums.rs:55:47
--> $DIR/capture-enums.rs:57:47
|
LL | let SingleVariant::Point(_, _, str) = point;
| ^^^^^
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ error[E0505]: cannot move out of `ts` because it is borrowed
LL | let _b = || { match ts {
| -- -- borrow occurs due to use in closure
| |
| borrow of `ts` occurs here
| borrow of `ts.x` occurs here
...
LL | let mut mut_ts = ts;
| ^^ move out of `ts` occurs here
Original file line number Diff line number Diff line change
@@ -64,9 +64,8 @@ fn test_6_should_capture_single_variant() {
//~^ ERROR First Pass analysis includes:
//~| ERROR Min Capture analysis includes:
match variant {
//~^ NOTE: Capturing variant[] -> Immutable
//~| NOTE: Capturing variant[(0, 0)] -> Immutable
//~| NOTE: Min Capture variant[] -> Immutable
//~^ NOTE: Capturing variant[(0, 0)] -> Immutable
//~| NOTE: Min Capture variant[(0, 0)] -> Immutable
SingleVariant::Points(a) => {
println!("{:?}", a);
}
@@ -149,8 +148,8 @@ fn test_7_should_capture_slice_len() {
//~^ ERROR First Pass analysis includes:
//~| ERROR Min Capture analysis includes:
match slice {
//~^ NOTE: Capturing slice[] -> Immutable
//~| NOTE: Min Capture slice[] -> Immutable
//~^ NOTE: Capturing slice[Deref] -> Immutable
//~| NOTE: Min Capture slice[Deref] -> Immutable
[_,_,_] => {},
_ => {}
}
@@ -161,8 +160,8 @@ fn test_7_should_capture_slice_len() {
//~^ ERROR First Pass analysis includes:
//~| ERROR Min Capture analysis includes:
match slice {
//~^ NOTE: Capturing slice[] -> Immutable
//~| NOTE: Min Capture slice[] -> Immutable
//~^ NOTE: Capturing slice[Deref] -> Immutable
//~| NOTE: Min Capture slice[Deref] -> Immutable
[] => {},
_ => {}
}
@@ -173,8 +172,8 @@ fn test_7_should_capture_slice_len() {
//~^ ERROR First Pass analysis includes:
//~| ERROR Min Capture analysis includes:
match slice {
//~^ NOTE: Capturing slice[] -> Immutable
//~| NOTE: Min Capture slice[] -> Immutable
//~^ NOTE: Capturing slice[Deref] -> Immutable
//~| NOTE: Min Capture slice[Deref] -> Immutable
[_, .. ,_] => {},
_ => {}
}
Original file line number Diff line number Diff line change
@@ -65,11 +65,6 @@ LL | | match variant {
LL | | };
| |_____^
|
note: Capturing variant[] -> Immutable
--> $DIR/patterns-capture-analysis.rs:66:15
|
LL | match variant {
| ^^^^^^^
note: Capturing variant[(0, 0)] -> Immutable
--> $DIR/patterns-capture-analysis.rs:66:15
|
@@ -87,14 +82,14 @@ LL | | match variant {
LL | | };
| |_____^
|
note: Min Capture variant[] -> Immutable
note: Min Capture variant[(0, 0)] -> Immutable
--> $DIR/patterns-capture-analysis.rs:66:15
|
LL | match variant {
| ^^^^^^^

error: First Pass analysis includes:
--> $DIR/patterns-capture-analysis.rs:83:5
--> $DIR/patterns-capture-analysis.rs:82:5
|
LL | / || {
LL | |
@@ -105,7 +100,7 @@ LL | | };
| |_____^

error: First Pass analysis includes:
--> $DIR/patterns-capture-analysis.rs:95:5
--> $DIR/patterns-capture-analysis.rs:94:5
|
LL | / || {
LL | |
@@ -116,7 +111,7 @@ LL | | };
| |_____^

error: First Pass analysis includes:
--> $DIR/patterns-capture-analysis.rs:108:5
--> $DIR/patterns-capture-analysis.rs:107:5
|
LL | / || {
LL | |
@@ -127,7 +122,7 @@ LL | | };
| |_____^

error: First Pass analysis includes:
--> $DIR/patterns-capture-analysis.rs:130:5
--> $DIR/patterns-capture-analysis.rs:129:5
|
LL | / || {
LL | |
@@ -138,13 +133,13 @@ LL | | };
| |_____^
|
note: Capturing variant[] -> Immutable
--> $DIR/patterns-capture-analysis.rs:133:15
--> $DIR/patterns-capture-analysis.rs:132:15
|
LL | match variant {
| ^^^^^^^

error: Min Capture analysis includes:
--> $DIR/patterns-capture-analysis.rs:130:5
--> $DIR/patterns-capture-analysis.rs:129:5
|
LL | / || {
LL | |
@@ -155,13 +150,13 @@ LL | | };
| |_____^
|
note: Min Capture variant[] -> Immutable
--> $DIR/patterns-capture-analysis.rs:133:15
--> $DIR/patterns-capture-analysis.rs:132:15
|
LL | match variant {
| ^^^^^^^

error: First Pass analysis includes:
--> $DIR/patterns-capture-analysis.rs:148:5
--> $DIR/patterns-capture-analysis.rs:147:5
|
LL | / || {
LL | |
@@ -171,14 +166,14 @@ LL | | match slice {
LL | | };
| |_____^
|
note: Capturing slice[] -> Immutable
--> $DIR/patterns-capture-analysis.rs:151:15
note: Capturing slice[Deref] -> Immutable
--> $DIR/patterns-capture-analysis.rs:150:15
|
LL | match slice {
| ^^^^^

error: Min Capture analysis includes:
--> $DIR/patterns-capture-analysis.rs:148:5
--> $DIR/patterns-capture-analysis.rs:147:5
|
LL | / || {
LL | |
@@ -188,14 +183,14 @@ LL | | match slice {
LL | | };
| |_____^
|
note: Min Capture slice[] -> Immutable
--> $DIR/patterns-capture-analysis.rs:151:15
note: Min Capture slice[Deref] -> Immutable
--> $DIR/patterns-capture-analysis.rs:150:15
|
LL | match slice {
| ^^^^^

error: First Pass analysis includes:
--> $DIR/patterns-capture-analysis.rs:160:5
--> $DIR/patterns-capture-analysis.rs:159:5
|
LL | / || {
LL | |
@@ -205,14 +200,14 @@ LL | | match slice {
LL | | };
| |_____^
|
note: Capturing slice[] -> Immutable
--> $DIR/patterns-capture-analysis.rs:163:15
note: Capturing slice[Deref] -> Immutable
--> $DIR/patterns-capture-analysis.rs:162:15
|
LL | match slice {
| ^^^^^

error: Min Capture analysis includes:
--> $DIR/patterns-capture-analysis.rs:160:5
--> $DIR/patterns-capture-analysis.rs:159:5
|
LL | / || {
LL | |
@@ -222,14 +217,14 @@ LL | | match slice {
LL | | };
| |_____^
|
note: Min Capture slice[] -> Immutable
--> $DIR/patterns-capture-analysis.rs:163:15
note: Min Capture slice[Deref] -> Immutable
--> $DIR/patterns-capture-analysis.rs:162:15
|
LL | match slice {
| ^^^^^

error: First Pass analysis includes:
--> $DIR/patterns-capture-analysis.rs:172:5
--> $DIR/patterns-capture-analysis.rs:171:5
|
LL | / || {
LL | |
@@ -239,14 +234,14 @@ LL | | match slice {
LL | | };
| |_____^
|
note: Capturing slice[] -> Immutable
--> $DIR/patterns-capture-analysis.rs:175:15
note: Capturing slice[Deref] -> Immutable
--> $DIR/patterns-capture-analysis.rs:174:15
|
LL | match slice {
| ^^^^^

error: Min Capture analysis includes:
--> $DIR/patterns-capture-analysis.rs:172:5
--> $DIR/patterns-capture-analysis.rs:171:5
|
LL | / || {
LL | |
@@ -256,14 +251,14 @@ LL | | match slice {
LL | | };
| |_____^
|
note: Min Capture slice[] -> Immutable
--> $DIR/patterns-capture-analysis.rs:175:15
note: Min Capture slice[Deref] -> Immutable
--> $DIR/patterns-capture-analysis.rs:174:15
|
LL | match slice {
| ^^^^^

error: First Pass analysis includes:
--> $DIR/patterns-capture-analysis.rs:189:5
--> $DIR/patterns-capture-analysis.rs:188:5
|
LL | / || {
LL | |
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// This example used to compile, but the fact that it should was never properly
// discussed. With further experience, we concluded that capture precision
// depending on whether some types are inhabited goes too far, introducing a
// bunch of headaches without much benefit.
//@ edition:2021
enum Void {}

pub fn main() {
let mut r = Result::<Void, (u32, u32)>::Err((0, 0));
let mut f = || {
let Err((ref mut a, _)) = r;
*a = 1;
};
let mut g = || {
//~^ ERROR: cannot borrow `r` as mutable more than once at a time
let Err((_, ref mut b)) = r;
*b = 2;
};
f();
g();
assert!(matches!(r, Err((1, 2))));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
error[E0499]: cannot borrow `r` as mutable more than once at a time
--> $DIR/only-inhabited-variant-stable.rs:14:17
|
LL | let mut f = || {
| -- first mutable borrow occurs here
LL | let Err((ref mut a, _)) = r;
| - first borrow occurs due to use of `r` in closure
...
LL | let mut g = || {
| ^^ second mutable borrow occurs here
LL |
LL | let Err((_, ref mut b)) = r;
| - second borrow occurs due to use of `r` in closure
...
LL | f();
| - first borrow later used here

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0499`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
error[E0499]: cannot borrow `r` as mutable more than once at a time
--> $DIR/only-inhabited-variant.rs:16:17
|
LL | let mut f = || {
| -- first mutable borrow occurs here
LL | let Err((ref mut a, _)) = r;
| - first borrow occurs due to use of `r` in closure
...
LL | let mut g = || {
| ^^ second mutable borrow occurs here
LL |
LL | let Err((_, ref mut b)) = r;
| - second borrow occurs due to use of `r` in closure
...
LL | f();
| - first borrow later used here

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0499`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
error[E0499]: cannot borrow `r` as mutable more than once at a time
--> $DIR/only-inhabited-variant.rs:16:17
|
LL | let mut f = || {
| -- first mutable borrow occurs here
LL | let Err((ref mut a, _)) = r;
| - first borrow occurs due to use of `r` in closure
...
LL | let mut g = || {
| ^^ second mutable borrow occurs here
LL |
LL | let Err((_, ref mut b)) = r;
| - second borrow occurs due to use of `r` in closure
...
LL | f();
| - first borrow later used here

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0499`.
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// Test precise capture of a multi-variant enum (when remaining variants are
// visibly uninhabited).
// This example used to compile, but the fact that it should was never properly
// discussed. With further experience, we concluded that capture precision
// depending on whether some types are inhabited goes too far, introducing a
// bunch of headaches without much benefit.
//@ revisions: normal exhaustive_patterns
//@ edition:2021
//@ run-pass
#![cfg_attr(exhaustive_patterns, feature(exhaustive_patterns))]
#![feature(never_type)]

@@ -13,6 +14,7 @@ pub fn main() {
*a = 1;
};
let mut g = || {
//~^ ERROR: cannot borrow `r` as mutable more than once at a time
let Err((_, ref mut b)) = r;
*b = 2;
};
41 changes: 41 additions & 0 deletions tests/ui/closures/at-pattern-weirdness-issue-137553.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//@ edition:2024
//@ check-pass

// Background:
fn f1() {
let mut a = (21, 37);
// only captures a.0, example compiles fine
let mut f = || {
let (ref mut x, _) = a;
*x = 42;
};
a.1 = 69;
f();
}

// This used to error out:
fn f2() {
let mut a = (21, 37);
// used to capture all of a, now captures only a.0
let mut f = || {
match a {
(ref mut x, _) => *x = 42,
}
};
a.1 = 69;
f();
}

// This was inconsistent with the following:
fn main() {
let mut a = (21, 37);
// the useless @-pattern would cause it to capture only a.0. now the
// behavior is consistent with the case that doesn't use the @-pattern
let mut f = || {
match a {
(ref mut x @ _, _) => *x = 42,
}
};
a.1 = 69;
f();
}
13 changes: 13 additions & 0 deletions tests/ui/closures/malformed-pattern-issue-140011.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//@compile-flags: -Wrust-2021-incompatible-closure-captures
enum B {
C(D), //~ ERROR: cannot find type `D` in this scope
E(F),
}
struct F;
fn f(h: B) {
|| {
let B::E(a) = h; //~ ERROR: refutable pattern in local binding
};
}

fn main() {}
36 changes: 36 additions & 0 deletions tests/ui/closures/malformed-pattern-issue-140011.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
error[E0412]: cannot find type `D` in this scope
--> $DIR/malformed-pattern-issue-140011.rs:3:7
|
LL | C(D),
| ^ not found in this scope
|
help: you might be missing a type parameter
|
LL | enum B<D> {
| +++

error[E0005]: refutable pattern in local binding
--> $DIR/malformed-pattern-issue-140011.rs:9:13
|
LL | let B::E(a) = h;
| ^^^^^^^ pattern `B::C(_)` not covered
|
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html
note: `B` defined here
--> $DIR/malformed-pattern-issue-140011.rs:2:6
|
LL | enum B {
| ^
LL | C(D),
| - not covered
= note: the matched value is of type `B`
help: you might want to use `let else` to handle the variant that isn't matched
|
LL | let B::E(a) = h else { todo!() };
| ++++++++++++++++

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0005, E0412.
For more information about an error, try `rustc --explain E0005`.
177 changes: 177 additions & 0 deletions tests/ui/closures/or-patterns-issue-137467.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
//@ edition:2024
//@ check-pass

const X: u32 = 0;

fn match_literal(x: (u32, u32, u32)) {
let _ = || {
let ((0, a, _) | (_, _, a)) = x;
a
};
}

fn match_range(x: (u32, u32, u32)) {
let _ = || {
let ((0..5, a, _) | (_, _, a)) = x;
a
};
}

fn match_const(x: (u32, u32, u32)) {
let _ = || {
let ((X, a, _) | (_, _, a)) = x;
a
};
}

// related testcase reported in #138973
fn without_bindings(x: u32) {
let _ = || {
let (0 | _) = x;
};
}

enum Choice { A, B }

fn match_unit_variant(x: (Choice, u32, u32)) {
let _ = || {
let ((Choice::A, a, _) | (Choice::B, _, a)) = x;
a
};
}

struct Unit;

fn match_unit_struct(mut x: (Unit, u32)) {
let r = &mut x.0;
let _ = || {
let (Unit, a) = x;
a
};

let _ = *r;
}

enum Also { Unit }

fn match_unit_enum(mut x: (Also, u32)) {
let r = &mut x.0;
let _ = || {
let (Also::Unit, a) = x;
a
};

let _ = *r;
}

enum TEnum {
A(u32),
B(u32),
}

enum SEnum {
A { a: u32 },
B { a: u32 },
}

fn match_tuple_enum(x: TEnum) {
let _ = || {
let (TEnum::A(a) | TEnum::B(a)) = x;
a
};
}

fn match_struct_enum(x: SEnum) {
let _ = || {
let (SEnum::A { a } | SEnum::B { a }) = x;
a
};
}

enum TSingle {
A(u32, u32),
}

enum SSingle {
A { a: u32, b: u32 },
}

struct TStruct(u32, u32);
struct SStruct { a: u32, b: u32 }

fn match_struct(mut x: SStruct) {
let r = &mut x.a;
let _ = || {
let SStruct { b, .. } = x;
b
};

let _ = *r;
}

fn match_tuple_struct(mut x: TStruct) {
let r = &mut x.0;
let _ = || {
let TStruct(_, a) = x;
a
};

let _ = *r;
}

fn match_singleton(mut x: SSingle) {
let SSingle::A { a: ref mut r, .. } = x;
let _ = || {
let SSingle::A { b, .. } = x;
b
};

let _ = *r;
}

fn match_tuple_singleton(mut x: TSingle) {
let TSingle::A(ref mut r, _) = x;
let _ = || {
let TSingle::A(_, a) = x;
a
};

let _ = *r;
}

fn match_slice(x: (&[u32], u32, u32)) {
let _ = || {
let (([], a, _) | ([_, ..], _, a)) = x;
a
};
}

// Original testcase, for completeness
enum Camera {
Normal { base_transform: i32 },
Volume { transform: i32 },
}

fn draw_ui(camera: &mut Camera) {
|| {
let (Camera::Normal {
base_transform: _transform,
}
| Camera::Volume {
transform: _transform,
}) = camera;
};
}

fn draw_ui2(camera: &mut Camera) {
|| {
let (Camera::Normal {
base_transform: _,
}
| Camera::Volume {
transform: _,
}) = camera;
};
}

fn main() {}
9 changes: 0 additions & 9 deletions tests/ui/type-alias-impl-trait/issue-96572-unconstrained.rs
Original file line number Diff line number Diff line change
@@ -23,15 +23,6 @@ fn upvar() {
};
}

fn enum_upvar() {
type T = impl Copy;
let foo: T = Some((1u32, 2u32));
let x = move || match foo {
None => (),
Some((a, b)) => (),
};
}

fn r#struct() {
#[derive(Copy, Clone)]
struct Foo((u32, u32));