Skip to content

Commit 7dab99c

Browse files
authored
Unrolled build for rust-lang#140022
Rollup merge of rust-lang#140022 - dianne:box-deref-pats, r=Nadrieril allow deref patterns to move out of boxes This adds a case to lower deref patterns on boxes using a built-in deref instead of a `Deref::deref` or `DerefMut::deref_mut` call: if `deref!(inner): Box<T>` is matching on place `place`, the inner pattern `inner` now matches on `*place` rather than a temporary. No longer needing to call a method also means it won't borrow the scrutinee in match arms. This allows for bindings in `inner` to move out of `*place`. For comparison with box patterns, this uses the same MIR lowering but different THIR. Consequently, deref patterns on boxes are treated the same as any other deref patterns in match exhaustiveness analysis. Box patterns can't quite be implemented in terms of deref patterns until exhaustiveness checking for deref patterns is implemented (I'll open a PR for exhaustiveness soon!). Tracking issue: rust-lang#87121 r? ``@Nadrieril``
2 parents 25cdf1f + 4e555fa commit 7dab99c

File tree

14 files changed

+229
-82
lines changed

14 files changed

+229
-82
lines changed

compiler/rustc_hir_typeck/src/expr_use_visitor.rs

+30-21
Original file line numberDiff line numberDiff line change
@@ -1000,13 +1000,15 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
10001000
// determines whether to borrow *at the level of the deref pattern* rather than
10011001
// borrowing the bound place (since that inner place is inside the temporary that
10021002
// stores the result of calling `deref()`/`deref_mut()` so can't be captured).
1003+
// Deref patterns on boxes don't borrow, so we ignore them here.
10031004
// HACK: this could be a fake pattern corresponding to a deref inserted by match
10041005
// ergonomics, in which case `pat.hir_id` will be the id of the subpattern.
1005-
let mutable = self.cx.typeck_results().pat_has_ref_mut_binding(subpattern);
1006-
let mutability =
1007-
if mutable { hir::Mutability::Mut } else { hir::Mutability::Not };
1008-
let bk = ty::BorrowKind::from_mutbl(mutability);
1009-
self.delegate.borrow_mut().borrow(place, discr_place.hir_id, bk);
1006+
if let hir::ByRef::Yes(mutability) =
1007+
self.cx.typeck_results().deref_pat_borrow_mode(place.place.ty(), subpattern)
1008+
{
1009+
let bk = ty::BorrowKind::from_mutbl(mutability);
1010+
self.delegate.borrow_mut().borrow(place, discr_place.hir_id, bk);
1011+
}
10101012
}
10111013
PatKind::Never => {
10121014
// A `!` pattern always counts as an immutable read of the discriminant,
@@ -1691,18 +1693,19 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
16911693
place_with_id = match adjust.kind {
16921694
adjustment::PatAdjust::BuiltinDeref => self.cat_deref(pat.hir_id, place_with_id)?,
16931695
adjustment::PatAdjust::OverloadedDeref => {
1694-
// This adjustment corresponds to an overloaded deref; it borrows the scrutinee to
1695-
// call `Deref::deref` or `DerefMut::deref_mut`. Invoke the callback before setting
1696-
// `place_with_id` to the temporary storing the result of the deref.
1696+
// This adjustment corresponds to an overloaded deref; unless it's on a box, it
1697+
// borrows the scrutinee to call `Deref::deref` or `DerefMut::deref_mut`. Invoke
1698+
// the callback before setting `place_with_id` to the temporary storing the
1699+
// result of the deref.
16971700
// HACK(dianne): giving the callback a fake deref pattern makes sure it behaves the
1698-
// same as it would if this were an explicit deref pattern.
1701+
// same as it would if this were an explicit deref pattern (including for boxes).
16991702
op(&place_with_id, &hir::Pat { kind: PatKind::Deref(pat), ..*pat })?;
17001703
let target_ty = match adjusts.peek() {
17011704
Some(&&next_adjust) => next_adjust.source,
17021705
// At the end of the deref chain, we get `pat`'s scrutinee.
17031706
None => self.pat_ty_unadjusted(pat)?,
17041707
};
1705-
self.pat_deref_temp(pat.hir_id, pat, target_ty)?
1708+
self.pat_deref_place(pat.hir_id, place_with_id, pat, target_ty)?
17061709
}
17071710
};
17081711
}
@@ -1810,7 +1813,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
18101813
}
18111814
PatKind::Deref(subpat) => {
18121815
let ty = self.pat_ty_adjusted(subpat)?;
1813-
let place = self.pat_deref_temp(pat.hir_id, subpat, ty)?;
1816+
let place = self.pat_deref_place(pat.hir_id, place_with_id, subpat, ty)?;
18141817
self.cat_pattern(place, subpat, op)?;
18151818
}
18161819

@@ -1863,21 +1866,27 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
18631866
Ok(())
18641867
}
18651868

1866-
/// Represents the place of the temp that stores the scrutinee of a deref pattern's interior.
1867-
fn pat_deref_temp(
1869+
/// Represents the place matched on by a deref pattern's interior.
1870+
fn pat_deref_place(
18681871
&self,
18691872
hir_id: HirId,
1873+
base_place: PlaceWithHirId<'tcx>,
18701874
inner: &hir::Pat<'_>,
18711875
target_ty: Ty<'tcx>,
18721876
) -> Result<PlaceWithHirId<'tcx>, Cx::Error> {
1873-
let mutable = self.cx.typeck_results().pat_has_ref_mut_binding(inner);
1874-
let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not };
1875-
let re_erased = self.cx.tcx().lifetimes.re_erased;
1876-
let ty = Ty::new_ref(self.cx.tcx(), re_erased, target_ty, mutability);
1877-
// A deref pattern stores the result of `Deref::deref` or `DerefMut::deref_mut` ...
1878-
let base = self.cat_rvalue(hir_id, ty);
1879-
// ... and the inner pattern matches on the place behind that reference.
1880-
self.cat_deref(hir_id, base)
1877+
match self.cx.typeck_results().deref_pat_borrow_mode(base_place.place.ty(), inner) {
1878+
// Deref patterns on boxes are lowered using a built-in deref.
1879+
hir::ByRef::No => self.cat_deref(hir_id, base_place),
1880+
// For other types, we create a temporary to match on.
1881+
hir::ByRef::Yes(mutability) => {
1882+
let re_erased = self.cx.tcx().lifetimes.re_erased;
1883+
let ty = Ty::new_ref(self.cx.tcx(), re_erased, target_ty, mutability);
1884+
// A deref pattern stores the result of `Deref::deref` or `DerefMut::deref_mut` ...
1885+
let base = self.cat_rvalue(hir_id, ty);
1886+
// ... and the inner pattern matches on the place behind that reference.
1887+
self.cat_deref(hir_id, base)
1888+
}
1889+
}
18811890
}
18821891

18831892
fn is_multivariant_adt(&self, ty: Ty<'tcx>, span: Span) -> bool {

compiler/rustc_middle/src/thir.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -799,7 +799,12 @@ pub enum PatKind<'tcx> {
799799
/// Deref pattern, written `box P` for now.
800800
DerefPattern {
801801
subpattern: Box<Pat<'tcx>>,
802-
mutability: hir::Mutability,
802+
/// Whether the pattern scrutinee needs to be borrowed in order to call `Deref::deref` or
803+
/// `DerefMut::deref_mut`, and if so, which. This is `ByRef::No` for deref patterns on
804+
/// boxes; they are lowered using a built-in deref rather than a method call, thus they
805+
/// don't borrow the scrutinee.
806+
#[type_visitable(ignore)]
807+
borrow: ByRef,
803808
},
804809

805810
/// One of the following:

compiler/rustc_middle/src/ty/typeck_results.rs

+15
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,21 @@ impl<'tcx> TypeckResults<'tcx> {
475475
has_ref_mut
476476
}
477477

478+
/// How should a deref pattern find the place for its inner pattern to match on?
479+
///
480+
/// In most cases, if the pattern recursively contains a `ref mut` binding, we find the inner
481+
/// pattern's scrutinee by calling `DerefMut::deref_mut`, and otherwise we call `Deref::deref`.
482+
/// However, for boxes we can use a built-in deref instead, which doesn't borrow the scrutinee;
483+
/// in this case, we return `ByRef::No`.
484+
pub fn deref_pat_borrow_mode(&self, pointer_ty: Ty<'_>, inner: &hir::Pat<'_>) -> ByRef {
485+
if pointer_ty.is_box() {
486+
ByRef::No
487+
} else {
488+
let mutable = self.pat_has_ref_mut_binding(inner);
489+
ByRef::Yes(if mutable { Mutability::Mut } else { Mutability::Not })
490+
}
491+
}
492+
478493
/// For a given closure, returns the iterator of `ty::CapturedPlace`s that are captured
479494
/// by the closure.
480495
pub fn closure_min_captures_flattened(

compiler/rustc_mir_build/src/builder/matches/match_pair.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::sync::Arc;
22

3+
use rustc_hir::ByRef;
34
use rustc_middle::mir::*;
45
use rustc_middle::thir::*;
56
use rustc_middle::ty::{self, Ty, TypeVisitableExt};
@@ -260,7 +261,13 @@ impl<'tcx> MatchPairTree<'tcx> {
260261
None
261262
}
262263

263-
PatKind::Deref { ref subpattern } => {
264+
PatKind::Deref { ref subpattern }
265+
| PatKind::DerefPattern { ref subpattern, borrow: ByRef::No } => {
266+
if cfg!(debug_assertions) && matches!(pattern.kind, PatKind::DerefPattern { .. }) {
267+
// Only deref patterns on boxes can be lowered using a built-in deref.
268+
debug_assert!(pattern.ty.is_box());
269+
}
270+
264271
MatchPairTree::for_pattern(
265272
place_builder.deref(),
266273
subpattern,
@@ -271,7 +278,7 @@ impl<'tcx> MatchPairTree<'tcx> {
271278
None
272279
}
273280

274-
PatKind::DerefPattern { ref subpattern, mutability } => {
281+
PatKind::DerefPattern { ref subpattern, borrow: ByRef::Yes(mutability) } => {
275282
// Create a new temporary for each deref pattern.
276283
// FIXME(deref_patterns): dedup temporaries to avoid multiple `deref()` calls?
277284
let temp = cx.temp(

compiler/rustc_mir_build/src/thir/pattern/mod.rs

+4-7
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
111111
let kind = match adjust.kind {
112112
PatAdjust::BuiltinDeref => PatKind::Deref { subpattern: thir_pat },
113113
PatAdjust::OverloadedDeref => {
114-
let mutable = self.typeck_results.pat_has_ref_mut_binding(pat);
115-
let mutability =
116-
if mutable { hir::Mutability::Mut } else { hir::Mutability::Not };
117-
PatKind::DerefPattern { subpattern: thir_pat, mutability }
114+
let borrow = self.typeck_results.deref_pat_borrow_mode(adjust.source, pat);
115+
PatKind::DerefPattern { subpattern: thir_pat, borrow }
118116
}
119117
};
120118
Box::new(Pat { span, ty: adjust.source, kind })
@@ -308,9 +306,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
308306
}
309307

310308
hir::PatKind::Deref(subpattern) => {
311-
let mutable = self.typeck_results.pat_has_ref_mut_binding(subpattern);
312-
let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not };
313-
PatKind::DerefPattern { subpattern: self.lower_pattern(subpattern), mutability }
309+
let borrow = self.typeck_results.deref_pat_borrow_mode(ty, subpattern);
310+
PatKind::DerefPattern { subpattern: self.lower_pattern(subpattern), borrow }
314311
}
315312
hir::PatKind::Ref(subpattern, _) => {
316313
// Track the default binding mode for the Rust 2024 migration suggestion.

src/doc/unstable-book/src/language-features/deref-patterns.md

+14-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ The tracking issue for this feature is: [#87121]
77
------------------------
88

99
> **Note**: This feature is incomplete. In the future, it is meant to supersede
10-
> [`box_patterns`](./box-patterns.md) and [`string_deref_patterns`](./string-deref-patterns.md).
10+
> [`box_patterns`] and [`string_deref_patterns`].
1111
1212
This feature permits pattern matching on [smart pointers in the standard library] through their
1313
`Deref` target types, either implicitly or with explicit `deref!(_)` patterns (the syntax of which
@@ -54,6 +54,17 @@ if let [b] = &mut *v {
5454
assert_eq!(v, [Box::new(Some(2))]);
5555
```
5656

57+
Like [`box_patterns`], deref patterns may move out of boxes:
58+
59+
```rust
60+
# #![feature(deref_patterns)]
61+
# #![allow(incomplete_features)]
62+
struct NoCopy;
63+
// Match exhaustiveness analysis is not yet implemented.
64+
let deref!(x) = Box::new(NoCopy) else { unreachable!() };
65+
drop::<NoCopy>(x);
66+
```
67+
5768
Additionally, when `deref_patterns` is enabled, string literal patterns may be written where `str`
5869
is expected. Likewise, byte string literal patterns may be written where `[u8]` or `[u8; _]` is
5970
expected. This lets them be used in `deref!(_)` patterns:
@@ -75,4 +86,6 @@ match *"test" {
7586

7687
Implicit deref pattern syntax is not yet supported for string or byte string literals.
7788

89+
[`box_patterns`]: ./box-patterns.md
90+
[`string_deref_patterns`]: ./string-deref-patterns.md
7891
[smart pointers in the standard library]: https://doc.rust-lang.org/std/ops/trait.DerefPure.html#implementors

tests/ui/pattern/deref-patterns/bindings.rs

+12-10
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
#![feature(deref_patterns)]
44
#![allow(incomplete_features)]
55

6+
use std::rc::Rc;
7+
68
#[cfg(explicit)]
79
fn simple_vec(vec: Vec<u32>) -> u32 {
810
match vec {
@@ -53,37 +55,37 @@ fn nested_vec(vecvec: Vec<Vec<u32>>) -> u32 {
5355

5456
#[cfg(explicit)]
5557
fn ref_mut(val: u32) -> u32 {
56-
let mut b = Box::new(0u32);
58+
let mut b = vec![0u32];
5759
match &mut b {
58-
deref!(_x) if false => unreachable!(),
59-
deref!(x) => {
60+
deref!([_x]) if false => unreachable!(),
61+
deref!([x]) => {
6062
*x = val;
6163
}
6264
_ => unreachable!(),
6365
}
64-
let deref!(x) = &b else { unreachable!() };
66+
let deref!([x]) = &b else { unreachable!() };
6567
*x
6668
}
6769

6870
#[cfg(implicit)]
6971
fn ref_mut(val: u32) -> u32 {
70-
let mut b = Box::new((0u32,));
72+
let mut b = vec![0u32];
7173
match &mut b {
72-
(_x,) if false => unreachable!(),
73-
(x,) => {
74+
[_x] if false => unreachable!(),
75+
[x] => {
7476
*x = val;
7577
}
7678
_ => unreachable!(),
7779
}
78-
let (x,) = &b else { unreachable!() };
80+
let [x] = &b else { unreachable!() };
7981
*x
8082
}
8183

8284
#[cfg(explicit)]
8385
#[rustfmt::skip]
8486
fn or_and_guard(tuple: (u32, u32)) -> u32 {
8587
let mut sum = 0;
86-
let b = Box::new(tuple);
88+
let b = Rc::new(tuple);
8789
match b {
8890
deref!((x, _) | (_, x)) if { sum += x; false } => {},
8991
_ => {},
@@ -95,7 +97,7 @@ fn or_and_guard(tuple: (u32, u32)) -> u32 {
9597
#[rustfmt::skip]
9698
fn or_and_guard(tuple: (u32, u32)) -> u32 {
9799
let mut sum = 0;
98-
let b = Box::new(tuple);
100+
let b = Rc::new(tuple);
99101
match b {
100102
(x, _) | (_, x) if { sum += x; false } => {},
101103
_ => {},

tests/ui/pattern/deref-patterns/cant_move_out_of_pattern.rs

+10-10
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ use std::rc::Rc;
55

66
struct Struct;
77

8-
fn cant_move_out_box(b: Box<Struct>) -> Struct {
8+
fn cant_move_out_vec(b: Vec<Struct>) -> Struct {
99
match b {
10-
//~^ ERROR: cannot move out of a shared reference
11-
deref!(x) => x,
12-
_ => unreachable!(),
10+
//~^ ERROR: cannot move out of type `[Struct]`, a non-copy slice
11+
deref!([x]) => x,
12+
_ => panic!(),
1313
}
1414
}
1515

@@ -21,16 +21,16 @@ fn cant_move_out_rc(rc: Rc<Struct>) -> Struct {
2121
}
2222
}
2323

24-
struct Container(Struct);
25-
26-
fn cant_move_out_box_implicit(b: Box<Container>) -> Struct {
24+
fn cant_move_out_vec_implicit(b: Vec<Struct>) -> Struct {
2725
match b {
28-
//~^ ERROR: cannot move out of a shared reference
29-
Container(x) => x,
30-
_ => unreachable!(),
26+
//~^ ERROR: cannot move out of type `[Struct]`, a non-copy slice
27+
[x] => x,
28+
_ => panic!(),
3129
}
3230
}
3331

32+
struct Container(Struct);
33+
3434
fn cant_move_out_rc_implicit(rc: Rc<Container>) -> Struct {
3535
match rc {
3636
//~^ ERROR: cannot move out of a shared reference

tests/ui/pattern/deref-patterns/cant_move_out_of_pattern.stderr

+21-20
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
1-
error[E0507]: cannot move out of a shared reference
1+
error[E0508]: cannot move out of type `[Struct]`, a non-copy slice
22
--> $DIR/cant_move_out_of_pattern.rs:9:11
33
|
44
LL | match b {
5-
| ^
5+
| ^ cannot move out of here
66
LL |
7-
LL | deref!(x) => x,
8-
| -
9-
| |
10-
| data moved here
11-
| move occurs because `x` has type `Struct`, which does not implement the `Copy` trait
7+
LL | deref!([x]) => x,
8+
| -
9+
| |
10+
| data moved here
11+
| move occurs because `x` has type `Struct`, which does not implement the `Copy` trait
1212
|
1313
help: consider borrowing the pattern binding
1414
|
15-
LL | deref!(ref x) => x,
16-
| +++
15+
LL | deref!([ref x]) => x,
16+
| +++
1717

1818
error[E0507]: cannot move out of a shared reference
1919
--> $DIR/cant_move_out_of_pattern.rs:17:11
@@ -32,22 +32,22 @@ help: consider borrowing the pattern binding
3232
LL | deref!(ref x) => x,
3333
| +++
3434

35-
error[E0507]: cannot move out of a shared reference
36-
--> $DIR/cant_move_out_of_pattern.rs:27:11
35+
error[E0508]: cannot move out of type `[Struct]`, a non-copy slice
36+
--> $DIR/cant_move_out_of_pattern.rs:25:11
3737
|
3838
LL | match b {
39-
| ^
39+
| ^ cannot move out of here
4040
LL |
41-
LL | Container(x) => x,
42-
| -
43-
| |
44-
| data moved here
45-
| move occurs because `x` has type `Struct`, which does not implement the `Copy` trait
41+
LL | [x] => x,
42+
| -
43+
| |
44+
| data moved here
45+
| move occurs because `x` has type `Struct`, which does not implement the `Copy` trait
4646
|
4747
help: consider borrowing the pattern binding
4848
|
49-
LL | Container(ref x) => x,
50-
| +++
49+
LL | [ref x] => x,
50+
| +++
5151

5252
error[E0507]: cannot move out of a shared reference
5353
--> $DIR/cant_move_out_of_pattern.rs:35:11
@@ -68,4 +68,5 @@ LL | Container(ref x) => x,
6868

6969
error: aborting due to 4 previous errors
7070

71-
For more information about this error, try `rustc --explain E0507`.
71+
Some errors have detailed explanations: E0507, E0508.
72+
For more information about an error, try `rustc --explain E0507`.

0 commit comments

Comments
 (0)