Skip to content
Open
Show file tree
Hide file tree
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
276 changes: 118 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
Expand Up @@ -761,6 +761,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// ],
/// }
/// ```
#[instrument(level = "debug", skip(self))]
fn compute_min_captures(
&self,
closure_def_id: LocalDefId,
Expand Down Expand Up @@ -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>,
Expand Down Expand Up @@ -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>,
Expand Down Expand Up @@ -2180,6 +2183,7 @@ fn restrict_precision_for_unsafe(
/// - 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,
Expand Down Expand Up @@ -2209,6 +2213,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,
Expand All @@ -2223,6 +2228,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,
Expand All @@ -2238,6 +2244,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,
Expand Down Expand Up @@ -2560,6 +2567,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,
Expand Down
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
Expand Up @@ -339,6 +339,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,
Expand Down
2 changes: 1 addition & 1 deletion src/tools/clippy/clippy_lints/src/methods/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -889,7 +889,7 @@ declare_clippy_lint! {
/// ```
#[clippy::version = "pre 1.29.0"]
pub SEARCH_IS_SOME,
complexity,
nursery,
"using an iterator or string search followed by `is_some()` or `is_none()`, which is more succinctly expressed as a call to `any()` or `contains()` (with negation in case of `is_none()`)"
}

Expand Down
1 change: 1 addition & 0 deletions src/tools/clippy/tests/ui/crashes/ice-9041.rs
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 {
Expand Down
2 changes: 1 addition & 1 deletion src/tools/clippy/tests/ui/crashes/ice-9041.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: called `is_some()` after searching an `Iterator` with `find`
--> tests/ui/crashes/ice-9041.rs:5:19
--> tests/ui/crashes/ice-9041.rs:6:19
|
LL | things.iter().find(|p| is_thing_ready(p)).is_some()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|p| is_thing_ready(&p))`
Expand Down
14 changes: 9 additions & 5 deletions src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -311,19 +311,23 @@ mod issue9120 {
}
}

// skip this test due to rust-lang/rust-clippy#16086
/*
#[allow(clippy::match_like_matches_macro)]
fn issue15102() {
let values = [None, Some(3)];
let has_even = values.iter().any(|v| matches!(&v, Some(x) if x % 2 == 0));
//~^ search_is_some
let has_even = values.iter().find(|v| matches!(v, Some(x) if x % 2 == 0)).is_some();
~^ search_is_some
println!("{has_even}");

let has_even = values
.iter()
.any(|v| match &v {
//~^ search_is_some
.find(|v| match v {
~^ search_is_some
Some(x) if x % 2 == 0 => true,
_ => false,
});
})
.is_some();
println!("{has_even}");
}
*/
7 changes: 5 additions & 2 deletions src/tools/clippy/tests/ui/search_is_some_fixable_some.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,20 +322,23 @@ mod issue9120 {
}
}

// skip this test due to rust-lang/rust-clippy#16086
/*
#[allow(clippy::match_like_matches_macro)]
fn issue15102() {
let values = [None, Some(3)];
let has_even = values.iter().find(|v| matches!(v, Some(x) if x % 2 == 0)).is_some();
//~^ search_is_some
~^ search_is_some
println!("{has_even}");

let has_even = values
.iter()
.find(|v| match v {
//~^ search_is_some
~^ search_is_some
Some(x) if x % 2 == 0 => true,
_ => false,
})
.is_some();
println!("{has_even}");
}
*/
29 changes: 1 addition & 28 deletions src/tools/clippy/tests/ui/search_is_some_fixable_some.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -346,32 +346,5 @@ error: called `is_some()` after searching an `Iterator` with `find`
LL | let _ = v.iter().find(|x: &&u32| (*arg_no_deref_dyn)(x)).is_some();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| (*arg_no_deref_dyn)(&x))`

error: called `is_some()` after searching an `Iterator` with `find`
--> tests/ui/search_is_some_fixable_some.rs:328:34
|
LL | let has_even = values.iter().find(|v| matches!(v, Some(x) if x % 2 == 0)).is_some();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|v| matches!(&v, Some(x) if x % 2 == 0))`

error: called `is_some()` after searching an `Iterator` with `find`
--> tests/ui/search_is_some_fixable_some.rs:334:10
|
LL | .find(|v| match v {
| __________^
LL | |
LL | | Some(x) if x % 2 == 0 => true,
LL | | _ => false,
LL | | })
LL | | .is_some();
| |__________________^
|
help: consider using
|
LL ~ .any(|v| match &v {
LL +
LL + Some(x) if x % 2 == 0 => true,
LL + _ => false,
LL ~ });
|

error: aborting due to 51 previous errors
error: aborting due to 49 previous errors

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 => {},
}
};
}
18 changes: 18 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,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

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, _) => {},
}
};
}
18 changes: 18 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,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

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 {
| ^ 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

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.

Loading
Loading