Auto merge of #138961 - meithecatte:expr-use-visitor, r=Nadrieril,traviscross,ChayimFriedman2
Make closure capturing have consistent and correct behaviour around patterns
Reference PR:
- https://github.com/rust-lang/reference/pull/1837
This PR has two goals:
- firstly, it fixes rust-lang/rust#137467. In order to do so, it needs to introduce a small breaking change surrounding the interaction of closure captures with matching against enums with uninhabited variants. Yes – to fix an ICE!
- this also fixes rust-lang/rust#138973, a slightly different case with the same root cause.
- likewise, fixes rust-lang/rust#140011.
- secondly, it fixes rust-lang/rust#137553, making the closure capturing rules consistent between `let` patterns and `match` patterns. This is new insta-stable behavior.
## Background
This change concerns how precise closure captures interact with patterns. As a little known feature, patterns that require inspecting only part of a value will only cause that part of the value to get captured:
```rust
fn main() {
let mut a = (21, 37);
// only captures a.0, writing to a.1 does not invalidate the closure
let mut f = || {
let (ref mut x, _) = a;
*x = 42;
};
a.1 = 69;
f();
}
```
I was not able to find any discussion of this behavior being introduced, or discussion of its edge-cases, but it is [documented in the Rust reference](https://doc.rust-lang.org/reference/types/closure.html#r-type.closure.capture.precision.wildcard).
The currently stable behavior is as follows:
- if any pattern contains a binding, the place it binds gets captured (implemented in current `walk_pat`)
- patterns in refutable positions (`match`, `if let`, `let ... else`, but not destructuring `let` or destructuring function parameters) get processed as follows (`maybe_read_scrutinee`):
- if matching against the pattern will at any point require inspecting a discriminant, or it includes a variable binding not followed by an ``@`-pattern,` capture *the entire scrutinee* by reference
You will note that this behavior is quite weird and it's hard to imagine a sensible rationale for at least some of its aspects. It has the following issues:
- firstly, it assumes that matching against an irrefutable pattern cannot possibly require inspecting any discriminants. With or-patterns, this isn't true, and it is the cause of the rust-lang/rust#137467 ICE.
- secondly, the presence of an ``@`-pattern` doesn't really have any semantics by itself. This is the weird behavior tracked as rust-lang/rust#137553.
- thirdly, the behavior is different between pattern-matching done through `let` and pattern-matching done through `match` – which is a superficial syntactic difference
This PR aims to address all of the above issues. The new behavior is as follows:
- like before, if a pattern contains a binding, the place it binds gets captured as required by the binding mode
- if matching against the pattern requires inspecting a disciminant, the place whose discriminant needs to be inspected gets captured by reference
"requires inspecting a discriminant" is also used here to mean "compare something with a constant" and other such decisions. For types other than ADTs, the details are not interesting and aren't changing.
## The breaking change
During closure capture analysis, matching an `enum` against a constructor is considered to require inspecting a discriminant if the `enum` has more than one variant. Notably, this is the case even if all the other variants happen to be uninhabited. This is motivated by implementation difficulties involved in querying whether types are inhabited before we're done with type inference – without moving mountains to make it happen, you hit this assert: 43f0014ef0/compiler/rustc_middle/src/ty/inhabitedness/mod.rs (L121)
Now, because the previous implementation did not concern itself with capturing the discriminants for irrefutable patterns at all, this is a breaking change – the following example, adapted from the testsuite, compiles on current stable, but will not compile with this PR:
```rust
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
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_eq!(r, Err((1, 2)));
}
```
## Is the breaking change necessary?
One other option would be to double down, and introduce a set of syntactic rules for determining whether a sub-pattern is in an irrefutable position, instead of querying the types and checking how many variants there are.
**This would not eliminate the breaking change,** but it would limit it to more contrived examples, such as
```rust
let ((true, Err((ref mut a, _, _))) | (false, Err((_, ref mut a, _)))) = x;
```
In this example, the `Err`s would not be considered in an irrefutable position, because they are part of an or-pattern. However, current stable would treat this just like a tuple `(bool, (T, U, _))`.
While introducing such a distinction would limit the impact, I would say that the added complexity would not be commensurate with the benefit it introduces.
## The new insta-stable behavior
If a pattern in a `match` expression or similar has parts it will never read, this part will not be captured anymore:
```rust
fn main() {
let mut a = (21, 37);
// now only captures a.0, instead of the whole a
let mut f = || {
match a {
(ref mut x, _) => *x = 42,
}
};
a.1 = 69;
f();
}
```
Note that this behavior was pretty much already present, but only accessible with this One Weird Trick™:
```rust
fn main() {
let mut a = (21, 37);
// both stable and this PR only capture a.0, because of the no-op `@-pattern`
let mut f = || {
match a {
(ref mut x @ _, _) => *x = 42,
}
};
a.1 = 69;
f();
}
```
## The second, more practically-relevant breaking change
After running crater, we have discovered that the aforementioned insta-stable behavior, where sometimes closures will now capture less, can also manifest as a breaking change. This is because it is possible that previously a closure would capture an entire struct by-move, and now it'll start capturing only part of it – some by move, and some by reference. This then causes the closure to have a more restrictive lifetime than it did previously.
See:
- https://github.com/rust-lang/rust/pull/138961#issuecomment-2761888557
- https://github.com/EC-labs/cec-assignment/pull/1
- https://github.com/tryandromeda/andromeda/pull/43
## Implementation notes
The PR has two main commits:
- "ExprUseVisitor: properly report discriminant reads" makes `walk_pat` perform all necessary capturing. This is the part that fixes rust-lang/rust#137467.
- "ExprUseVisitor: remove maybe_read_scrutinee" removes the unnecessary "capture the entire scrutinee" behavior, fixing rust-lang/rust#137553.
The new logic stops making the distinction between one particular example that used to work, and another ICE, tracked as rust-lang/rust#119786. As this requires an unstable feature, I am leaving this as future work.
This commit is contained in:
commit
ed0006a7ba
41 changed files with 809 additions and 342 deletions
|
|
@ -7,9 +7,7 @@
|
|||
|
||||
use std::cell::{Ref, RefCell};
|
||||
use std::ops::Deref;
|
||||
use std::slice::from_ref;
|
||||
|
||||
use hir::Expr;
|
||||
use hir::def::DefKind;
|
||||
use hir::pat_util::EnumerateAndAdjustIterator as _;
|
||||
use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx};
|
||||
|
|
@ -313,7 +311,8 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
|
|||
|
||||
let param_place = self.cat_rvalue(param.hir_id, param_ty);
|
||||
|
||||
self.walk_irrefutable_pat(¶m_place, param.pat)?;
|
||||
self.fake_read_scrutinee(¶m_place, false)?;
|
||||
self.walk_pat(¶m_place, param.pat, false)?;
|
||||
}
|
||||
|
||||
self.consume_expr(body.value)?;
|
||||
|
|
@ -455,13 +454,9 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
|
|||
|
||||
hir::ExprKind::Match(discr, arms, _) => {
|
||||
let discr_place = self.cat_expr(discr)?;
|
||||
self.maybe_read_scrutinee(
|
||||
discr,
|
||||
discr_place.clone(),
|
||||
arms.iter().map(|arm| arm.pat),
|
||||
)?;
|
||||
self.fake_read_scrutinee(&discr_place, true)?;
|
||||
self.walk_expr(discr)?;
|
||||
|
||||
// treatment of the discriminant is handled while walking the arms.
|
||||
for arm in arms {
|
||||
self.walk_arm(&discr_place, arm)?;
|
||||
}
|
||||
|
|
@ -598,116 +593,25 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn maybe_read_scrutinee<'t>(
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
fn fake_read_scrutinee(
|
||||
&self,
|
||||
discr: &Expr<'_>,
|
||||
discr_place: PlaceWithHirId<'tcx>,
|
||||
pats: impl Iterator<Item = &'t hir::Pat<'t>>,
|
||||
discr_place: &PlaceWithHirId<'tcx>,
|
||||
refutable: bool,
|
||||
) -> Result<(), Cx::Error> {
|
||||
// Matching should not always be considered a use of the place, hence
|
||||
// discr does not necessarily need to be borrowed.
|
||||
// We only want to borrow discr if the pattern contain something other
|
||||
// than wildcards.
|
||||
let mut needs_to_be_read = false;
|
||||
for pat in pats {
|
||||
self.cat_pattern(discr_place.clone(), pat, &mut |place, pat| {
|
||||
match &pat.kind {
|
||||
PatKind::Missing => unreachable!(),
|
||||
PatKind::Binding(.., opt_sub_pat) => {
|
||||
// If the opt_sub_pat is None, then the binding does not count as
|
||||
// a wildcard for the purpose of borrowing discr.
|
||||
if opt_sub_pat.is_none() {
|
||||
needs_to_be_read = true;
|
||||
}
|
||||
}
|
||||
PatKind::Never => {
|
||||
// A never pattern reads the value.
|
||||
// FIXME(never_patterns): does this do what I expect?
|
||||
needs_to_be_read = true;
|
||||
}
|
||||
PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, span }) => {
|
||||
// A `Path` pattern is just a name like `Foo`. This is either a
|
||||
// named constant or else it refers to an ADT variant
|
||||
let closure_def_id = match discr_place.place.base {
|
||||
PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let res = self.cx.typeck_results().qpath_res(qpath, *hir_id);
|
||||
match res {
|
||||
Res::Def(DefKind::Const, _) | Res::Def(DefKind::AssocConst, _) => {
|
||||
// Named constants have to be equated with the value
|
||||
// being matched, so that's a read of the value being matched.
|
||||
//
|
||||
// FIXME: We don't actually reads for ZSTs.
|
||||
needs_to_be_read = true;
|
||||
}
|
||||
_ => {
|
||||
// Otherwise, this is a struct/enum variant, and so it's
|
||||
// only a read if we need to read the discriminant.
|
||||
needs_to_be_read |=
|
||||
self.is_multivariant_adt(place.place.ty(), *span);
|
||||
}
|
||||
}
|
||||
}
|
||||
PatKind::TupleStruct(..) | PatKind::Struct(..) | PatKind::Tuple(..) => {
|
||||
// For `Foo(..)`, `Foo { ... }` and `(...)` patterns, check if we are matching
|
||||
// against a multivariant enum or struct. In that case, we have to read
|
||||
// the discriminant. Otherwise this kind of pattern doesn't actually
|
||||
// read anything (we'll get invoked for the `...`, which may indeed
|
||||
// perform some reads).
|
||||
|
||||
let place_ty = place.place.ty();
|
||||
needs_to_be_read |= self.is_multivariant_adt(place_ty, pat.span);
|
||||
}
|
||||
PatKind::Expr(_) | PatKind::Range(..) => {
|
||||
// If the PatKind is a Lit or a Range then we want
|
||||
// to borrow discr.
|
||||
needs_to_be_read = true;
|
||||
}
|
||||
PatKind::Slice(lhs, wild, rhs) => {
|
||||
// We don't need to test the length if the pattern is `[..]`
|
||||
if matches!((lhs, wild, rhs), (&[], Some(_), &[]))
|
||||
// Arrays have a statically known size, so
|
||||
// there is no need to read their length
|
||||
|| place.place.ty().peel_refs().is_array()
|
||||
{
|
||||
} else {
|
||||
needs_to_be_read = true;
|
||||
}
|
||||
}
|
||||
PatKind::Or(_)
|
||||
| PatKind::Box(_)
|
||||
| PatKind::Deref(_)
|
||||
| PatKind::Ref(..)
|
||||
| PatKind::Guard(..)
|
||||
| PatKind::Wild
|
||||
| PatKind::Err(_) => {
|
||||
// If the PatKind is Or, Box, or Ref, the decision is made later
|
||||
// as these patterns contains subpatterns
|
||||
// If the PatKind is Wild or Err, the decision is made based on the other patterns
|
||||
// being examined
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?
|
||||
}
|
||||
|
||||
if needs_to_be_read {
|
||||
self.borrow_expr(discr, BorrowKind::Immutable)?;
|
||||
let cause = if refutable {
|
||||
FakeReadCause::ForMatchedPlace(closure_def_id)
|
||||
} else {
|
||||
let closure_def_id = match discr_place.place.base {
|
||||
PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id),
|
||||
_ => None,
|
||||
};
|
||||
FakeReadCause::ForLet(closure_def_id)
|
||||
};
|
||||
|
||||
self.delegate.borrow_mut().fake_read(
|
||||
&discr_place,
|
||||
FakeReadCause::ForMatchedPlace(closure_def_id),
|
||||
discr_place.hir_id,
|
||||
);
|
||||
self.delegate.borrow_mut().fake_read(discr_place, cause, discr_place.hir_id);
|
||||
|
||||
// We always want to walk the discriminant. We want to make sure, for instance,
|
||||
// that the discriminant has been initialized.
|
||||
self.walk_expr(discr)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -724,12 +628,11 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
|
|||
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 {
|
||||
// borrowing because we need to test the discriminant
|
||||
self.maybe_read_scrutinee(expr, expr_place.clone(), from_ref(pat).iter())?;
|
||||
self.walk_block(els)?;
|
||||
}
|
||||
self.walk_irrefutable_pat(&expr_place, pat)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -901,16 +804,6 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
|
|||
discr_place: &PlaceWithHirId<'tcx>,
|
||||
arm: &hir::Arm<'_>,
|
||||
) -> Result<(), Cx::Error> {
|
||||
let closure_def_id = match discr_place.place.base {
|
||||
PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
self.delegate.borrow_mut().fake_read(
|
||||
discr_place,
|
||||
FakeReadCause::ForMatchedPlace(closure_def_id),
|
||||
discr_place.hir_id,
|
||||
);
|
||||
self.walk_pat(discr_place, arm.pat, arm.guard.is_some())?;
|
||||
|
||||
if let Some(ref e) = arm.guard {
|
||||
|
|
@ -921,28 +814,20 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Walks a pat that occurs in isolation (i.e., top-level of fn argument or
|
||||
/// let binding, and *not* a match arm or nested pat.)
|
||||
fn walk_irrefutable_pat(
|
||||
&self,
|
||||
discr_place: &PlaceWithHirId<'tcx>,
|
||||
pat: &hir::Pat<'_>,
|
||||
) -> Result<(), Cx::Error> {
|
||||
let closure_def_id = match discr_place.place.base {
|
||||
PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
self.delegate.borrow_mut().fake_read(
|
||||
discr_place,
|
||||
FakeReadCause::ForLet(closure_def_id),
|
||||
discr_place.hir_id,
|
||||
);
|
||||
self.walk_pat(discr_place, pat, false)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// The core driver for walking a pattern
|
||||
///
|
||||
/// This should mirror how pattern-matching gets lowered to MIR, as
|
||||
/// otherwise lowering will ICE when trying to resolve the upvars.
|
||||
///
|
||||
/// However, it is okay to approximate it here by doing *more* accesses than
|
||||
/// the actual MIR builder will, which is useful when some checks are too
|
||||
/// cumbersome to perform here. For example, if after typeck it becomes
|
||||
/// clear that only one variant of an enum is inhabited, and therefore a
|
||||
/// read of the discriminant is not necessary, `walk_pat` will have
|
||||
/// over-approximated the necessary upvar capture granularity.
|
||||
///
|
||||
/// Do note that discrepancies like these do still create obscure corners
|
||||
/// in the semantics of the language, and should be avoided if possible.
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
fn walk_pat(
|
||||
&self,
|
||||
|
|
@ -952,6 +837,11 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
|
|||
) -> Result<(), Cx::Error> {
|
||||
let tcx = self.cx.tcx();
|
||||
self.cat_pattern(discr_place.clone(), pat, &mut |place, pat| {
|
||||
debug!("walk_pat: pat.kind={:?}", pat.kind);
|
||||
let read_discriminant = || {
|
||||
self.delegate.borrow_mut().borrow(place, discr_place.hir_id, BorrowKind::Immutable);
|
||||
};
|
||||
|
||||
match pat.kind {
|
||||
PatKind::Binding(_, canonical_id, ..) => {
|
||||
debug!("walk_pat: binding place={:?} pat={:?}", place, pat);
|
||||
|
|
@ -974,11 +864,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
|
|||
// binding when lowering pattern guards to ensure that the guard does not
|
||||
// modify the scrutinee.
|
||||
if has_guard {
|
||||
self.delegate.borrow_mut().borrow(
|
||||
place,
|
||||
discr_place.hir_id,
|
||||
BorrowKind::Immutable,
|
||||
);
|
||||
read_discriminant();
|
||||
}
|
||||
|
||||
// It is also a borrow or copy/move of the value being matched.
|
||||
|
|
@ -1014,13 +900,73 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
|
|||
PatKind::Never => {
|
||||
// A `!` pattern always counts as an immutable read of the discriminant,
|
||||
// even in an irrefutable pattern.
|
||||
self.delegate.borrow_mut().borrow(
|
||||
place,
|
||||
discr_place.hir_id,
|
||||
BorrowKind::Immutable,
|
||||
);
|
||||
read_discriminant();
|
||||
}
|
||||
PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, span }) => {
|
||||
// A `Path` pattern is just a name like `Foo`. This is either a
|
||||
// named constant or else it refers to an ADT variant
|
||||
|
||||
let res = self.cx.typeck_results().qpath_res(qpath, *hir_id);
|
||||
match res {
|
||||
Res::Def(DefKind::Const, _) | Res::Def(DefKind::AssocConst, _) => {
|
||||
// Named constants have to be equated with the value
|
||||
// being matched, so that's a read of the value being matched.
|
||||
//
|
||||
// FIXME: Does the MIR code skip this read when matching on a ZST?
|
||||
// If so, we can also skip it here.
|
||||
read_discriminant();
|
||||
}
|
||||
_ => {
|
||||
// Otherwise, this is a struct/enum variant, and so it's
|
||||
// only a read if we need to read the discriminant.
|
||||
if self.is_multivariant_adt(place.place.ty(), *span) {
|
||||
read_discriminant();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
PatKind::Expr(_) | PatKind::Range(..) => {
|
||||
// When matching against a literal or range, we need to
|
||||
// borrow the place to compare it against the pattern.
|
||||
//
|
||||
// Note that we do this read even if the range matches all
|
||||
// possible values, such as 0..=u8::MAX. This is because
|
||||
// we don't want to depend on consteval here.
|
||||
//
|
||||
// FIXME: What if the type being matched only has one
|
||||
// possible value?
|
||||
read_discriminant();
|
||||
}
|
||||
PatKind::Struct(..) | PatKind::TupleStruct(..) => {
|
||||
if self.is_multivariant_adt(place.place.ty(), pat.span) {
|
||||
read_discriminant();
|
||||
}
|
||||
}
|
||||
PatKind::Slice(lhs, wild, rhs) => {
|
||||
// We don't need to test the length if the pattern is `[..]`
|
||||
if matches!((lhs, wild, rhs), (&[], Some(_), &[]))
|
||||
// Arrays have a statically known size, so
|
||||
// there is no need to read their length
|
||||
|| place.place.ty().peel_refs().is_array()
|
||||
{
|
||||
// No read necessary
|
||||
} else {
|
||||
read_discriminant();
|
||||
}
|
||||
}
|
||||
PatKind::Or(_)
|
||||
| PatKind::Box(_)
|
||||
| PatKind::Ref(..)
|
||||
| PatKind::Guard(..)
|
||||
| PatKind::Tuple(..)
|
||||
| PatKind::Wild
|
||||
| PatKind::Missing
|
||||
| PatKind::Err(_) => {
|
||||
// If the PatKind is Or, Box, Ref, Guard, or Tuple, the relevant accesses
|
||||
// are made later as these patterns contains subpatterns.
|
||||
// If the PatKind is Missing, Wild or Err, any relevant accesses are made when processing
|
||||
// the other patterns that are part of the match
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
@ -1904,6 +1850,20 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
|
|||
}
|
||||
}
|
||||
|
||||
/// Checks whether a type has multiple variants, and therefore, whether a
|
||||
/// read of the discriminant might be necessary. Note that the actual MIR
|
||||
/// builder code does a more specific check, filtering out variants that
|
||||
/// happen to be uninhabited.
|
||||
///
|
||||
/// Here, it is not practical to perform such a check, because inhabitedness
|
||||
/// queries require typeck results, and typeck requires closure capture analysis.
|
||||
///
|
||||
/// Moreover, the language is moving towards uninhabited variants still semantically
|
||||
/// causing a discriminant read, so we *shouldn't* perform any such check.
|
||||
///
|
||||
/// FIXME(never_patterns): update this comment once the aforementioned MIR builder
|
||||
/// code is changed to be insensitive to inhhabitedness.
|
||||
#[instrument(skip(self, span), level = "debug")]
|
||||
fn is_multivariant_adt(&self, ty: Ty<'tcx>, span: Span) -> bool {
|
||||
if let ty::Adt(def, _) = self.cx.structurally_resolve_type(span, ty).kind() {
|
||||
// Note that if a non-exhaustive SingleVariant is defined in another crate, we need
|
||||
|
|
|
|||
|
|
@ -761,6 +761,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
/// ],
|
||||
/// }
|
||||
/// ```
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
fn compute_min_captures(
|
||||
&self,
|
||||
closure_def_id: LocalDefId,
|
||||
|
|
@ -2029,6 +2030,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>,
|
||||
|
|
@ -2119,6 +2121,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 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,
|
||||
|
|
@ -2559,6 +2566,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,
|
||||
|
|
|
|||
|
|
@ -343,6 +343,12 @@ impl<'tcx> MatchPairTree<'tcx> {
|
|||
|
||||
if let Some(testable_case) = testable_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,
|
||||
testable_case,
|
||||
|
|
|
|||
|
|
@ -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()`)"
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![warn(clippy::search_is_some)]
|
||||
pub struct Thing;
|
||||
//@no-rustfix
|
||||
pub fn has_thing(things: &[Thing]) -> bool {
|
||||
|
|
|
|||
|
|
@ -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))`
|
||||
|
|
|
|||
|
|
@ -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}");
|
||||
}
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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}");
|
||||
}
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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
src/tools/miri/tests/fail/closures/deref-in-pattern.rs
Normal file
20
src/tools/miri/tests/fail/closures/deref-in-pattern.rs
Normal file
|
|
@ -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
src/tools/miri/tests/fail/closures/deref-in-pattern.stderr
Normal file
18
src/tools/miri/tests/fail/closures/deref-in-pattern.stderr
Normal file
|
|
@ -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
src/tools/miri/tests/fail/closures/partial-pattern.rs
Normal file
28
src/tools/miri/tests/fail/closures/partial-pattern.rs
Normal file
|
|
@ -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
src/tools/miri/tests/fail/closures/partial-pattern.stderr
Normal file
18
src/tools/miri/tests/fail/closures/partial-pattern.stderr
Normal file
|
|
@ -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
src/tools/miri/tests/fail/closures/uninhabited-variant.rs
Normal file
31
src/tools/miri/tests/fail/closures/uninhabited-variant.rs
Normal file
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -166,6 +166,9 @@ fn capture_specific_fields() {
|
|||
*a + x + (*b as i64)
|
||||
}
|
||||
}
|
||||
// FIXME: These tests currently fail, because rust-analyzer hasn't yet implemented the changes
|
||||
// introduced in rust-lang/rust#138961. See rust-lang/rust-analyzer#21274 for more discussion.
|
||||
/*
|
||||
size_and_align_expr! {
|
||||
struct X(i64, i32, (u8, i128));
|
||||
let y: X = X(2, 5, (7, 3));
|
||||
|
|
@ -183,6 +186,7 @@ fn capture_specific_fields() {
|
|||
a + x + (b as i64)
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -194,6 +198,9 @@ fn match_pattern() {
|
|||
x
|
||||
}
|
||||
}
|
||||
// FIXME: These tests currently fail, because rust-analyzer hasn't yet implemented the changes
|
||||
// introduced in rust-lang/rust#138961. See rust-lang/rust-analyzer#21274 for more discussion.
|
||||
/*
|
||||
size_and_align_expr! {
|
||||
minicore: copy;
|
||||
stmts: [
|
||||
|
|
@ -206,6 +213,7 @@ fn match_pattern() {
|
|||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
size_and_align_expr! {
|
||||
minicore: copy;
|
||||
stmts: [
|
||||
|
|
|
|||
15
tests/crashes/119786-2.rs
Normal file
15
tests/crashes/119786-2.rs
Normal file
|
|
@ -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
tests/crashes/119786-3.rs
Normal file
15
tests/crashes/119786-3.rs
Normal file
|
|
@ -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() {}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
//@ known-bug: #137467
|
||||
//@ edition: 2021
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
//@ known-bug: #137467
|
||||
//@ edition: 2021
|
||||
|
||||
enum Camera {
|
||||
Normal { base_transform: i32 },
|
||||
Volume { transform: i32 },
|
||||
}
|
||||
|
||||
fn draw_ui(camera: &mut Camera) {
|
||||
|| {
|
||||
let (Camera::Normal {
|
||||
base_transform: _,
|
||||
}
|
||||
| Camera::Volume {
|
||||
transform: _,
|
||||
}) = camera;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
//@ known-bug: #137467
|
||||
//@ edition: 2021
|
||||
|
||||
fn meow(x: (u32, u32, u32)) {
|
||||
let f = || {
|
||||
let ((0, a, _) | (_, _, a)) = x;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
//@ known-bug: #140011
|
||||
//@compile-flags: -Wrust-2021-incompatible-closure-captures
|
||||
enum b {
|
||||
c(d),
|
||||
e(f),
|
||||
}
|
||||
struct f;
|
||||
fn g() {
|
||||
let h;
|
||||
|| b::e(a) = h;
|
||||
}
|
||||
|
|
@ -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,17 +30,15 @@ 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: {
|
||||
|
|
@ -50,10 +46,9 @@ yields ()
|
|||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ 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);
|
||||
|
|
@ -29,6 +30,7 @@ fn multi_variant_enum() {
|
|||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
| ^^^^^
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
// Newly accepted examples as a result of the changes introduced in #138961.
|
||||
//
|
||||
//@ edition:2024
|
||||
//@ check-pass
|
||||
#![allow(unused_assignments)]
|
||||
|
||||
// Reading the length as part of a pattern captures the pointee.
|
||||
fn f() {
|
||||
let mut x: &mut [u8] = &mut [1, 2, 3];
|
||||
let c = || {
|
||||
match x {
|
||||
[] => (),
|
||||
_ => (),
|
||||
}
|
||||
};
|
||||
x = &mut [];
|
||||
c();
|
||||
}
|
||||
|
||||
// Plain old deref as part of pattern behaves similarly
|
||||
fn g() {
|
||||
let mut x: &mut bool = &mut false;
|
||||
let mut t = true;
|
||||
let c = || {
|
||||
match x {
|
||||
true => (),
|
||||
false => (),
|
||||
}
|
||||
};
|
||||
x = &mut t;
|
||||
c();
|
||||
}
|
||||
|
||||
// Like f, but the lifetime implications are expressed in terms of
|
||||
// returning a closure.
|
||||
fn f2<'l: 's, 's>(x: &'s mut &'l [u8]) -> impl Fn() + 'l {
|
||||
|| match *x {
|
||||
&[] => (),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
// Related testcase that was already accepted before
|
||||
fn f3<'l: 's, 's>(x: &'s mut &'l [u8]) -> impl Fn() + 'l {
|
||||
|| match **x {
|
||||
[] => (),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
f();
|
||||
g();
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
[_, .. ,_] => {},
|
||||
_ => {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 | |
|
||||
|
|
|
|||
|
|
@ -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))));
|
||||
}
|
||||
|
|
@ -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`.
|
||||
|
|
@ -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`.
|
||||
|
|
@ -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`.
|
||||
|
|
@ -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
tests/ui/closures/at-pattern-weirdness-issue-137553.rs
Normal file
41
tests/ui/closures/at-pattern-weirdness-issue-137553.rs
Normal file
|
|
@ -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
tests/ui/closures/malformed-pattern-issue-140011.rs
Normal file
13
tests/ui/closures/malformed-pattern-issue-140011.rs
Normal file
|
|
@ -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
tests/ui/closures/malformed-pattern-issue-140011.stderr
Normal file
36
tests/ui/closures/malformed-pattern-issue-140011.stderr
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
error[E0425]: 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, E0425.
|
||||
For more information about an error, try `rustc --explain E0005`.
|
||||
177
tests/ui/closures/or-patterns-issue-137467.rs
Normal file
177
tests/ui/closures/or-patterns-issue-137467.rs
Normal file
|
|
@ -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() {}
|
||||
|
|
@ -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));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue