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
13 changes: 4 additions & 9 deletions compiler/rustc_hir_typeck/src/expr_use_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
}

hir::ExprKind::Let(hir::LetExpr { pat, init, .. }) => {
self.walk_local(init, pat, None, || self.borrow_expr(init, BorrowKind::Immutable))?;
self.walk_local(init, pat, None)?;
}

hir::ExprKind::Match(discr, arms, _) => {
Expand Down Expand Up @@ -577,7 +577,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
fn walk_stmt(&self, stmt: &hir::Stmt<'_>) -> Result<(), Cx::Error> {
match stmt.kind {
hir::StmtKind::Let(hir::LetStmt { pat, init: Some(expr), els, .. }) => {
self.walk_local(expr, pat, *els, || Ok(()))?;
self.walk_local(expr, pat, *els)?;
}

hir::StmtKind::Let(_) => {}
Expand Down Expand Up @@ -617,19 +617,14 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
Ok(())
}

fn walk_local<F>(
fn walk_local(
&self,
expr: &hir::Expr<'_>,
pat: &hir::Pat<'_>,
els: Option<&hir::Block<'_>>,
mut f: F,
) -> Result<(), Cx::Error>
where
F: FnMut() -> Result<(), Cx::Error>,
{
) -> Result<(), Cx::Error> {
self.walk_expr(expr)?;
let expr_place = self.cat_expr(expr)?;
f()?;
self.fake_read_scrutinee(&expr_place, els.is_some())?;
self.walk_pat(&expr_place, pat, false)?;
if let Some(els) = els {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// This test serves to document the change in semantics introduced by
// rust-lang/rust#138961, extended to `if let` closure captures.
//
// 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.
//
// Before rust-lang/rust#154210, `if let` in closures captured all of `x`, so
// this test did not fail because the closure is never called.
//@normalize-stderr-test: "constructing invalid value of type [^:]+:" -> "constructing invalid value:"

#![allow(irrefutable_let_patterns)]

fn main() {
// the inner reference is dangling
let x: &&u32 = unsafe {
let x: u32 = 42;
&&*&raw const x
};

//~v ERROR: encountered a dangling reference
let _ = || {
if let &&_y = x {}
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
error: Undefined Behavior: constructing invalid value: encountered a dangling reference (use-after-free)
--> tests/fail/match/closures/if-let-deref-in-pattern.rs:LL:CC
|
LL | let _ = || {
| _____________^
LL | | if let &&_y = x {}
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

33 changes: 33 additions & 0 deletions src/tools/miri/tests/fail/match/closures/if-let-partial-pattern.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// This test serves to document the change in semantics introduced by
// rust-lang/rust#138961, extended to `if let` closure captures.
//
// 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.
//
// Before rust-lang/rust#154210, `if let` in closures captured all of `x`, so
// this test did not fail because the closure is never called.
//@normalize-stderr-test: "constructing invalid value of type [^:]+:" -> "constructing invalid value:"

#![allow(irrefutable_let_patterns)]

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)
};

//~v ERROR: encountered a dangling reference
let _ = || {
if let (&_y, _) = x {}
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
error: Undefined Behavior: constructing invalid value: encountered a dangling reference (use-after-free)
--> tests/fail/match/closures/if-let-partial-pattern.rs:LL:CC
|
LL | let _ = || {
| _____________^
LL | | if let (&_y, _) = x {}
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

2 changes: 0 additions & 2 deletions tests/ui/closures/2229_closure_analysis/capture-enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,13 @@ 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);
Expand Down
24 changes: 7 additions & 17 deletions tests/ui/closures/2229_closure_analysis/capture-enums.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,18 @@ note: Capturing point[] -> Immutable
|
LL | if let Info::Point(_, _, str) = point {
| ^^^^^
note: Capturing point[] -> Immutable
--> $DIR/capture-enums.rs:21:41
|
LL | if let Info::Point(_, _, str) = point {
| ^^^^^
note: Capturing point[(2, 0)] -> ByValue
--> $DIR/capture-enums.rs:21:41
|
LL | if let Info::Point(_, _, str) = point {
| ^^^^^
note: Capturing meta[] -> Immutable
--> $DIR/capture-enums.rs:29:35
|
LL | if let Info::Meta(_, v) = meta {
| ^^^^
note: Capturing meta[] -> Immutable
--> $DIR/capture-enums.rs:29:35
--> $DIR/capture-enums.rs:28:35
|
LL | if let Info::Meta(_, v) = meta {
| ^^^^
note: Capturing meta[(1, 1)] -> ByValue
--> $DIR/capture-enums.rs:29:35
--> $DIR/capture-enums.rs:28:35
|
LL | if let Info::Meta(_, v) = meta {
| ^^^^
Expand All @@ -57,13 +47,13 @@ note: Min Capture point[] -> ByValue
LL | if let Info::Point(_, _, str) = point {
| ^^^^^
note: Min Capture meta[] -> ByValue
--> $DIR/capture-enums.rs:29:35
--> $DIR/capture-enums.rs:28:35
|
LL | if let Info::Meta(_, v) = meta {
| ^^^^

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

error: Min Capture analysis includes:
--> $DIR/capture-enums.rs:49:5
--> $DIR/capture-enums.rs:47:5
|
LL | / || {
LL | |
Expand All @@ -93,7 +83,7 @@ LL | | };
| |_____^
|
note: Min Capture point[(2, 0)] -> ByValue
--> $DIR/capture-enums.rs:52:47
--> $DIR/capture-enums.rs:50:47
|
LL | let SingleVariant::Point(_, _, str) = point;
| ^^^^^
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//@ edition:2021

#![feature(rustc_attrs)]
#![feature(stmt_expr_attributes)]
#![allow(irrefutable_let_patterns)]

enum SingleVariant {
Pair(i32, String),
}

fn if_let_closure() {
let variant = SingleVariant::Pair(1, "hello".into());

let c = #[rustc_capture_analysis]
|| {
//~^ ERROR First Pass analysis includes:
//~| ERROR Min Capture analysis includes:
if let SingleVariant::Pair(ref n, s) = variant {
//~^ NOTE: Capturing variant[(0, 0)] -> Immutable
//~| NOTE: Capturing variant[(1, 0)] -> ByValue
//~| NOTE: Min Capture variant[(0, 0)] -> Immutable
//~| NOTE: Min Capture variant[(1, 0)] -> ByValue
let _ = (n, s);
}
};

c();
}

fn match_closure() {
let variant = SingleVariant::Pair(1, "hello".into());

let c = #[rustc_capture_analysis]
|| {
//~^ ERROR First Pass analysis includes:
//~| ERROR Min Capture analysis includes:
match variant {
//~^ NOTE: Capturing variant[(0, 0)] -> Immutable
//~| NOTE: Capturing variant[(1, 0)] -> ByValue
//~| NOTE: Min Capture variant[(0, 0)] -> Immutable
//~| NOTE: Min Capture variant[(1, 0)] -> ByValue
SingleVariant::Pair(ref n, s) => {
let _ = (n, s);
}
}
};

c();
}

fn main() {
if_let_closure();
match_closure();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
error: First Pass analysis includes:
--> $DIR/if-let-patterns-capture-analysis.rs:15:5
|
LL | / || {
LL | |
LL | |
LL | | if let SingleVariant::Pair(ref n, s) = variant {
... |
LL | | };
| |_____^
|
note: Capturing variant[(0, 0)] -> Immutable
--> $DIR/if-let-patterns-capture-analysis.rs:18:48
|
LL | if let SingleVariant::Pair(ref n, s) = variant {
| ^^^^^^^
note: Capturing variant[(1, 0)] -> ByValue
--> $DIR/if-let-patterns-capture-analysis.rs:18:48
|
LL | if let SingleVariant::Pair(ref n, s) = variant {
| ^^^^^^^

error: Min Capture analysis includes:
--> $DIR/if-let-patterns-capture-analysis.rs:15:5
|
LL | / || {
LL | |
LL | |
LL | | if let SingleVariant::Pair(ref n, s) = variant {
... |
LL | | };
| |_____^
|
note: Min Capture variant[(0, 0)] -> Immutable
--> $DIR/if-let-patterns-capture-analysis.rs:18:48
|
LL | if let SingleVariant::Pair(ref n, s) = variant {
| ^^^^^^^
note: Min Capture variant[(1, 0)] -> ByValue
--> $DIR/if-let-patterns-capture-analysis.rs:18:48
|
LL | if let SingleVariant::Pair(ref n, s) = variant {
| ^^^^^^^

error: First Pass analysis includes:
--> $DIR/if-let-patterns-capture-analysis.rs:34:5
|
LL | / || {
LL | |
LL | |
LL | | match variant {
... |
LL | | };
| |_____^
|
note: Capturing variant[(0, 0)] -> Immutable
--> $DIR/if-let-patterns-capture-analysis.rs:37:15
|
LL | match variant {
| ^^^^^^^
note: Capturing variant[(1, 0)] -> ByValue
--> $DIR/if-let-patterns-capture-analysis.rs:37:15
|
LL | match variant {
| ^^^^^^^

error: Min Capture analysis includes:
--> $DIR/if-let-patterns-capture-analysis.rs:34:5
|
LL | / || {
LL | |
LL | |
LL | | match variant {
... |
LL | | };
| |_____^
|
note: Min Capture variant[(0, 0)] -> Immutable
--> $DIR/if-let-patterns-capture-analysis.rs:37:15
|
LL | match variant {
| ^^^^^^^
note: Min Capture variant[(1, 0)] -> ByValue
--> $DIR/if-let-patterns-capture-analysis.rs:37:15
|
LL | match variant {
| ^^^^^^^

error: aborting due to 4 previous errors

Loading
Loading