Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit ce93617

Browse files
committed
Auto merge of rust-lang#14851 - HKalbasi:mir, r=HKalbasi
Handle match scrutinee in closure captures fix rust-lang#14754
2 parents e8ae2d3 + 60379da commit ce93617

File tree

9 files changed

+166
-12
lines changed

9 files changed

+166
-12
lines changed

crates/hir-def/src/body.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -221,15 +221,15 @@ impl Body {
221221

222222
pub fn walk_bindings_in_pat(&self, pat_id: PatId, mut f: impl FnMut(BindingId)) {
223223
self.walk_pats(pat_id, &mut |pat| {
224-
if let Pat::Bind { id, .. } = pat {
224+
if let Pat::Bind { id, .. } = &self[pat] {
225225
f(*id);
226226
}
227227
});
228228
}
229229

230-
pub fn walk_pats(&self, pat_id: PatId, f: &mut impl FnMut(&Pat)) {
230+
pub fn walk_pats(&self, pat_id: PatId, f: &mut impl FnMut(PatId)) {
231231
let pat = &self[pat_id];
232-
f(pat);
232+
f(pat_id);
233233
match pat {
234234
Pat::Range { .. }
235235
| Pat::Lit(..)

crates/hir-ty/src/diagnostics/match_check.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ impl<'a> PatCtxt<'a> {
147147
}
148148

149149
hir_def::hir::Pat::Bind { id, subpat, .. } => {
150-
let bm = self.infer.pat_binding_modes[&pat];
150+
let bm = self.infer.binding_modes[id];
151151
ty = &self.infer[id];
152152
let name = &self.body.bindings[id].name;
153153
match (bm, ty.kind(Interner)) {

crates/hir-ty/src/infer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ pub struct InferenceResult {
389389
standard_types: InternedStandardTypes,
390390
/// Stores the types which were implicitly dereferenced in pattern binding modes.
391391
pub pat_adjustments: FxHashMap<PatId, Vec<Ty>>,
392-
pub pat_binding_modes: FxHashMap<PatId, BindingMode>,
392+
pub binding_modes: ArenaMap<BindingId, BindingMode>,
393393
pub expr_adjustments: FxHashMap<ExprId, Vec<Adjustment>>,
394394
pub(crate) closure_info: FxHashMap<ClosureId, (Vec<CapturedItem>, FnTrait)>,
395395
// FIXME: remove this field

crates/hir-ty/src/infer/closure.rs

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -504,9 +504,27 @@ impl InferenceContext<'_> {
504504
self.consume_exprs(args.iter().copied());
505505
}
506506
Expr::Match { expr, arms } => {
507-
self.consume_expr(*expr);
508507
for arm in arms.iter() {
509508
self.consume_expr(arm.expr);
509+
if let Some(guard) = arm.guard {
510+
self.consume_expr(guard);
511+
}
512+
}
513+
self.walk_expr(*expr);
514+
if let Some(discr_place) = self.place_of_expr(*expr) {
515+
if self.is_upvar(&discr_place) {
516+
let mut capture_mode = None;
517+
for arm in arms.iter() {
518+
self.walk_pat(&mut capture_mode, arm.pat);
519+
}
520+
if let Some(c) = capture_mode {
521+
self.push_capture(CapturedItemWithoutTy {
522+
place: discr_place,
523+
kind: c,
524+
span: (*expr).into(),
525+
})
526+
}
527+
}
510528
}
511529
}
512530
Expr::Break { expr, label: _ }
@@ -618,6 +636,57 @@ impl InferenceContext<'_> {
618636
}
619637
}
620638

639+
fn walk_pat(&mut self, result: &mut Option<CaptureKind>, pat: PatId) {
640+
let mut update_result = |ck: CaptureKind| match result {
641+
Some(r) => {
642+
*r = cmp::max(*r, ck);
643+
}
644+
None => *result = Some(ck),
645+
};
646+
self.body.walk_pats(pat, &mut |p| match &self.body[p] {
647+
Pat::Ref { .. }
648+
| Pat::Box { .. }
649+
| Pat::Missing
650+
| Pat::Wild
651+
| Pat::Tuple { .. }
652+
| Pat::Or(_) => (),
653+
Pat::TupleStruct { .. } | Pat::Record { .. } => {
654+
if let Some(variant) = self.result.variant_resolution_for_pat(p) {
655+
let adt = variant.adt_id();
656+
let is_multivariant = match adt {
657+
hir_def::AdtId::EnumId(e) => self.db.enum_data(e).variants.len() != 1,
658+
_ => false,
659+
};
660+
if is_multivariant {
661+
update_result(CaptureKind::ByRef(BorrowKind::Shared));
662+
}
663+
}
664+
}
665+
Pat::Slice { .. }
666+
| Pat::ConstBlock(_)
667+
| Pat::Path(_)
668+
| Pat::Lit(_)
669+
| Pat::Range { .. } => {
670+
update_result(CaptureKind::ByRef(BorrowKind::Shared));
671+
}
672+
Pat::Bind { id, .. } => match self.result.binding_modes[*id] {
673+
crate::BindingMode::Move => {
674+
if self.is_ty_copy(self.result.type_of_binding[*id].clone()) {
675+
update_result(CaptureKind::ByRef(BorrowKind::Shared));
676+
} else {
677+
update_result(CaptureKind::ByValue);
678+
}
679+
}
680+
crate::BindingMode::Ref(r) => match r {
681+
Mutability::Mut => update_result(CaptureKind::ByRef(BorrowKind::Mut {
682+
allow_two_phase_borrow: false,
683+
})),
684+
Mutability::Not => update_result(CaptureKind::ByRef(BorrowKind::Shared)),
685+
},
686+
},
687+
});
688+
}
689+
621690
fn expr_ty(&self, expr: ExprId) -> Ty {
622691
self.result[expr].clone()
623692
}
@@ -641,14 +710,14 @@ impl InferenceContext<'_> {
641710
false
642711
}
643712

644-
fn is_ty_copy(&self, ty: Ty) -> bool {
713+
fn is_ty_copy(&mut self, ty: Ty) -> bool {
645714
if let TyKind::Closure(id, _) = ty.kind(Interner) {
646715
// FIXME: We handle closure as a special case, since chalk consider every closure as copy. We
647716
// should probably let chalk know which closures are copy, but I don't know how doing it
648717
// without creating query cycles.
649718
return self.result.closure_info.get(id).map(|x| x.1 == FnTrait::Fn).unwrap_or(true);
650719
}
651-
ty.is_copy(self.db, self.owner)
720+
self.table.resolve_completely(ty).is_copy(self.db, self.owner)
652721
}
653722

654723
fn select_from_expr(&mut self, expr: ExprId) {

crates/hir-ty/src/infer/pat.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ impl<'a> InferenceContext<'a> {
340340
} else {
341341
BindingMode::convert(mode)
342342
};
343-
self.result.pat_binding_modes.insert(pat, mode);
343+
self.result.binding_modes.insert(binding, mode);
344344

345345
let inner_ty = match subpat {
346346
Some(subpat) => self.infer_pat(subpat, &expected, default_bm),
@@ -439,7 +439,7 @@ fn is_non_ref_pat(body: &hir_def::body::Body, pat: PatId) -> bool {
439439
pub(super) fn contains_explicit_ref_binding(body: &Body, pat_id: PatId) -> bool {
440440
let mut res = false;
441441
body.walk_pats(pat_id, &mut |pat| {
442-
res |= matches!(pat, Pat::Bind { id, .. } if body.bindings[*id].mode == BindingAnnotation::Ref);
442+
res |= matches!(body[pat], Pat::Bind { id, .. } if body.bindings[id].mode == BindingAnnotation::Ref);
443443
});
444444
res
445445
}

crates/hir-ty/src/layout/tests/closure.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,43 @@ fn capture_specific_fields() {
182182
}
183183
}
184184

185+
#[test]
186+
fn match_pattern() {
187+
size_and_align_expr! {
188+
struct X(i64, i32, (u8, i128));
189+
let y: X = X(2, 5, (7, 3));
190+
move |x: i64| {
191+
match y {
192+
_ => x,
193+
}
194+
}
195+
}
196+
size_and_align_expr! {
197+
minicore: copy;
198+
stmts: [
199+
struct X(i64, i32, (u8, i128));
200+
let y: X = X(2, 5, (7, 3));
201+
]
202+
|x: i64| {
203+
match y {
204+
X(_a, _b, _c) => x,
205+
}
206+
}
207+
}
208+
size_and_align_expr! {
209+
minicore: copy;
210+
stmts: [
211+
struct X(i64, i32, (u8, i128));
212+
let y: X = X(2, 5, (7, 3));
213+
]
214+
|x: i64| {
215+
match y {
216+
_y => x,
217+
}
218+
}
219+
}
220+
}
221+
185222
#[test]
186223
fn ellipsis_pattern() {
187224
size_and_align_expr! {

crates/hir/src/source_analyzer.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,9 +236,9 @@ impl SourceAnalyzer {
236236
_db: &dyn HirDatabase,
237237
pat: &ast::IdentPat,
238238
) -> Option<BindingMode> {
239-
let pat_id = self.pat_id(&pat.clone().into())?;
239+
let binding_id = self.binding_id_of_pat(pat)?;
240240
let infer = self.infer.as_ref()?;
241-
infer.pat_binding_modes.get(&pat_id).map(|bm| match bm {
241+
infer.binding_modes.get(binding_id).map(|bm| match bm {
242242
hir_ty::BindingMode::Move => BindingMode::Move,
243243
hir_ty::BindingMode::Ref(hir_ty::Mutability::Mut) => BindingMode::Ref(Mutability::Mut),
244244
hir_ty::BindingMode::Ref(hir_ty::Mutability::Not) => {

crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,4 +151,25 @@ fn f(x: &mut X<'_>) {
151151
"#,
152152
);
153153
}
154+
155+
#[test]
156+
fn no_false_positive_match_and_closure_capture() {
157+
check_diagnostics(
158+
r#"
159+
//- minicore: copy, fn
160+
enum X {
161+
Foo(u16),
162+
Bar,
163+
}
164+
165+
fn main() {
166+
let x = &X::Bar;
167+
let c = || match *x {
168+
X::Foo(t) => t,
169+
_ => 5,
170+
};
171+
}
172+
"#,
173+
);
174+
}
154175
}

crates/ide/src/hover/tests.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,33 @@ fn main() {
301301
* `(*x.f2.0.0).f` by mutable borrow
302302
"#]],
303303
);
304+
check(
305+
r#"
306+
//- minicore: copy, option
307+
308+
fn do_char(c: char) {}
309+
310+
fn main() {
311+
let x = None;
312+
let y = |$0| {
313+
match x {
314+
Some(c) => do_char(c),
315+
None => x = None,
316+
}
317+
};
318+
}
319+
"#,
320+
expect![[r#"
321+
*|*
322+
```rust
323+
{closure#0} // size = 8, align = 8
324+
impl FnMut()
325+
```
326+
327+
## Captures
328+
* `x` by mutable borrow
329+
"#]],
330+
);
304331
}
305332

306333
#[test]

0 commit comments

Comments
 (0)