Skip to content

Commit be61862

Browse files
Nadrierilfee1-dead
andcommitted
Lower deref patterns to MIR
This handles using deref patterns to choose the correct match arm. This does not handle bindings or guards. Co-authored-by: Deadbeef <[email protected]>
1 parent 12fdd42 commit be61862

File tree

11 files changed

+259
-39
lines changed

11 files changed

+259
-39
lines changed

compiler/rustc_hir_typeck/src/mem_categorization.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -715,13 +715,19 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
715715
self.cat_pattern_(place_with_id, subpat, op)?;
716716
}
717717

718-
PatKind::Box(subpat) | PatKind::Ref(subpat, _) | PatKind::Deref(subpat) => {
718+
PatKind::Box(subpat) | PatKind::Ref(subpat, _) => {
719719
// box p1, &p1, &mut p1. we can ignore the mutability of
720720
// PatKind::Ref since that information is already contained
721721
// in the type.
722722
let subplace = self.cat_deref(pat, place_with_id)?;
723723
self.cat_pattern_(subplace, subpat, op)?;
724724
}
725+
PatKind::Deref(subpat) => {
726+
let ty = self.pat_ty_adjusted(subpat)?;
727+
// A deref pattern generates a temporary.
728+
let place = self.cat_rvalue(pat.hir_id, ty);
729+
self.cat_pattern_(place, subpat, op)?;
730+
}
725731

726732
PatKind::Slice(before, ref slice, after) => {
727733
let Some(element_ty) = place_with_id.place.ty().builtin_index() else {

compiler/rustc_mir_build/src/build/matches/mod.rs

+7
Original file line numberDiff line numberDiff line change
@@ -1168,6 +1168,7 @@ enum TestCase<'pat, 'tcx> {
11681168
Constant { value: mir::Const<'tcx> },
11691169
Range(&'pat PatRange<'tcx>),
11701170
Slice { len: usize, variable_length: bool },
1171+
Deref { temp: Place<'tcx> },
11711172
Or { pats: Box<[FlatPat<'pat, 'tcx>]> },
11721173
}
11731174

@@ -1227,6 +1228,12 @@ enum TestKind<'tcx> {
12271228

12281229
/// Test that the length of the slice is equal to `len`.
12291230
Len { len: u64, op: BinOp },
1231+
1232+
/// Call `Deref::deref` on the value.
1233+
Deref {
1234+
/// Temporary to store the result of `deref()`.
1235+
temp: Place<'tcx>,
1236+
},
12301237
}
12311238

12321239
/// A test to perform to determine which [`Candidate`] matches a value.

compiler/rustc_mir_build/src/build/matches/test.rs

+61-27
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
4242
TestKind::Len { len: len as u64, op }
4343
}
4444

45+
TestCase::Deref { temp } => TestKind::Deref { temp },
46+
4547
TestCase::Or { .. } => bug!("or-patterns should have already been handled"),
4648

4749
TestCase::Irrefutable { .. } => span_bug!(
@@ -143,35 +145,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
143145
);
144146
}
145147
let re_erased = tcx.lifetimes.re_erased;
146-
let ref_string = self.temp(Ty::new_imm_ref(tcx, re_erased, ty), test.span);
147148
let ref_str_ty = Ty::new_imm_ref(tcx, re_erased, tcx.types.str_);
148149
let ref_str = self.temp(ref_str_ty, test.span);
149-
let deref = tcx.require_lang_item(LangItem::Deref, None);
150-
let method = trait_method(tcx, deref, sym::deref, [ty]);
151150
let eq_block = self.cfg.start_new_block();
152-
self.cfg.push_assign(
153-
block,
154-
source_info,
155-
ref_string,
156-
Rvalue::Ref(re_erased, BorrowKind::Shared, place),
157-
);
158-
self.cfg.terminate(
159-
block,
160-
source_info,
161-
TerminatorKind::Call {
162-
func: Operand::Constant(Box::new(ConstOperand {
163-
span: test.span,
164-
user_ty: None,
165-
const_: method,
166-
})),
167-
args: vec![Spanned { node: Operand::Move(ref_string), span: DUMMY_SP }],
168-
destination: ref_str,
169-
target: Some(eq_block),
170-
unwind: UnwindAction::Continue,
171-
call_source: CallSource::Misc,
172-
fn_span: source_info.span,
173-
},
174-
);
151+
// `let ref_str: &str = <String as Deref>::deref(&place);`
152+
self.call_deref(block, eq_block, place, ty, ref_str, test.span);
175153
self.non_scalar_compare(
176154
eq_block,
177155
success_block,
@@ -270,9 +248,57 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
270248
Operand::Move(expected),
271249
);
272250
}
251+
252+
TestKind::Deref { temp } => {
253+
let ty = place_ty.ty;
254+
let target = target_block(TestBranch::Success);
255+
self.call_deref(block, target, place, ty, temp, test.span);
256+
}
273257
}
274258
}
275259

260+
/// Perform `let temp = <ty as Deref>::deref(&place)`.
261+
pub(super) fn call_deref(
262+
&mut self,
263+
block: BasicBlock,
264+
target_block: BasicBlock,
265+
place: Place<'tcx>,
266+
ty: Ty<'tcx>,
267+
temp: Place<'tcx>,
268+
span: Span,
269+
) {
270+
let source_info = self.source_info(span);
271+
let re_erased = self.tcx.lifetimes.re_erased;
272+
let deref = self.tcx.require_lang_item(LangItem::Deref, None);
273+
let method = trait_method(self.tcx, deref, sym::deref, [ty]);
274+
let ref_src = self.temp(Ty::new_imm_ref(self.tcx, re_erased, ty), span);
275+
// `let ref_src = &src_place;`
276+
self.cfg.push_assign(
277+
block,
278+
source_info,
279+
ref_src,
280+
Rvalue::Ref(re_erased, BorrowKind::Shared, place),
281+
);
282+
// `let temp = <Ty as Deref>::deref(ref_src);`
283+
self.cfg.terminate(
284+
block,
285+
source_info,
286+
TerminatorKind::Call {
287+
func: Operand::Constant(Box::new(ConstOperand {
288+
span,
289+
user_ty: None,
290+
const_: method,
291+
})),
292+
args: vec![Spanned { node: Operand::Move(ref_src), span }],
293+
destination: temp,
294+
target: Some(target_block),
295+
unwind: UnwindAction::Continue,
296+
call_source: CallSource::Misc,
297+
fn_span: source_info.span,
298+
},
299+
);
300+
}
301+
276302
/// Compare using the provided built-in comparison operator
277303
fn compare(
278304
&mut self,
@@ -660,13 +686,21 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
660686
}
661687
}
662688

689+
(TestKind::Deref { temp: test_temp }, TestCase::Deref { temp })
690+
if test_temp == temp =>
691+
{
692+
fully_matched = true;
693+
Some(TestBranch::Success)
694+
}
695+
663696
(
664697
TestKind::Switch { .. }
665698
| TestKind::SwitchInt { .. }
666699
| TestKind::If
667700
| TestKind::Len { .. }
668701
| TestKind::Range { .. }
669-
| TestKind::Eq { .. },
702+
| TestKind::Eq { .. }
703+
| TestKind::Deref { .. },
670704
_,
671705
) => {
672706
fully_matched = false;

compiler/rustc_mir_build/src/build/matches/util.rs

+10-5
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ use rustc_data_structures::fx::FxIndexSet;
55
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
66
use rustc_middle::mir::*;
77
use rustc_middle::thir::{self, *};
8-
use rustc_middle::ty;
98
use rustc_middle::ty::TypeVisitableExt;
9+
use rustc_middle::ty::{self, Ty};
1010

1111
impl<'a, 'tcx> Builder<'a, 'tcx> {
1212
pub(crate) fn field_match_pairs<'pat>(
@@ -250,10 +250,15 @@ impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {
250250
default_irrefutable()
251251
}
252252

253-
PatKind::DerefPattern { .. } => {
254-
// FIXME(deref_patterns)
255-
// Treat it like a wildcard for now.
256-
default_irrefutable()
253+
PatKind::DerefPattern { ref subpattern } => {
254+
// Create a new temporary for each deref pattern.
255+
// FIXME(deref_patterns): dedup temporaries to avoid multiple `deref()` calls?
256+
let temp = cx.temp(
257+
Ty::new_imm_ref(cx.tcx, cx.tcx.lifetimes.re_erased, subpattern.ty),
258+
pattern.span,
259+
);
260+
subpairs.push(MatchPair::new(PlaceBuilder::from(temp).deref(), subpattern, cx));
261+
TestCase::Deref { temp }
257262
}
258263
};
259264

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//@ run-pass
2+
#![feature(deref_patterns)]
3+
#![allow(incomplete_features)]
4+
5+
fn simple_vec(vec: Vec<u32>) -> u32 {
6+
match vec {
7+
deref!([]) => 100,
8+
// FIXME(deref_patterns): fake borrows break guards
9+
// deref!([x]) if x == 4 => x + 4,
10+
deref!([x]) => x,
11+
deref!([1, x]) => x + 200,
12+
deref!(ref slice) => slice.iter().sum(),
13+
_ => 2000,
14+
}
15+
}
16+
17+
fn nested_vec(vecvec: Vec<Vec<u32>>) -> u32 {
18+
match vecvec {
19+
deref!([]) => 0,
20+
deref!([deref!([x])]) => x,
21+
deref!([deref!([0, x]) | deref!([1, x])]) => x,
22+
deref!([ref x]) => x.iter().sum(),
23+
deref!([deref!([]), deref!([1, x, y])]) => y - x,
24+
_ => 2000,
25+
}
26+
}
27+
28+
fn main() {
29+
assert_eq!(simple_vec(vec![1]), 1);
30+
assert_eq!(simple_vec(vec![1, 2]), 202);
31+
assert_eq!(simple_vec(vec![1, 2, 3]), 6);
32+
33+
assert_eq!(nested_vec(vec![vec![0, 42]]), 42);
34+
assert_eq!(nested_vec(vec![vec![1, 42]]), 42);
35+
assert_eq!(nested_vec(vec![vec![1, 2, 3]]), 6);
36+
assert_eq!(nested_vec(vec![vec![], vec![1, 2, 3]]), 1);
37+
}
+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//@ run-pass
2+
// Test the execution of deref patterns.
3+
#![feature(deref_patterns)]
4+
#![allow(incomplete_features)]
5+
6+
fn branch(vec: Vec<u32>) -> u32 {
7+
match vec {
8+
deref!([]) => 0,
9+
deref!([1, _, 3]) => 1,
10+
deref!([2, ..]) => 2,
11+
_ => 1000,
12+
}
13+
}
14+
15+
fn nested(vec: Vec<Vec<u32>>) -> u32 {
16+
match vec {
17+
deref!([deref!([]), ..]) => 1,
18+
deref!([deref!([0, ..]), deref!([1, ..])]) => 2,
19+
_ => 1000,
20+
}
21+
}
22+
23+
fn main() {
24+
assert!(matches!(Vec::<u32>::new(), deref!([])));
25+
assert!(matches!(vec![1], deref!([1])));
26+
assert!(matches!(&vec![1], deref!([1])));
27+
assert!(matches!(vec![&1], deref!([1])));
28+
assert!(matches!(vec![vec![1]], deref!([deref!([1])])));
29+
30+
assert_eq!(branch(vec![]), 0);
31+
assert_eq!(branch(vec![1, 2, 3]), 1);
32+
assert_eq!(branch(vec![3, 2, 1]), 1000);
33+
assert_eq!(branch(vec![2]), 2);
34+
assert_eq!(branch(vec![2, 3]), 2);
35+
assert_eq!(branch(vec![3, 2]), 1000);
36+
37+
assert_eq!(nested(vec![vec![], vec![2]]), 1);
38+
assert_eq!(nested(vec![vec![0], vec![1]]), 2);
39+
assert_eq!(nested(vec![vec![0, 2], vec![1, 2]]), 2);
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#![feature(deref_patterns)]
2+
#![allow(incomplete_features)]
3+
4+
use std::rc::Rc;
5+
6+
struct Struct;
7+
8+
fn cant_move_out_box(b: Box<Struct>) -> Struct {
9+
match b {
10+
//~^ ERROR: cannot move out of a shared reference
11+
deref!(x) => x,
12+
_ => unreachable!(),
13+
}
14+
}
15+
16+
fn cant_move_out_rc(rc: Rc<Struct>) -> Struct {
17+
match rc {
18+
//~^ ERROR: cannot move out of a shared reference
19+
deref!(x) => x,
20+
_ => unreachable!(),
21+
}
22+
}
23+
24+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
error[E0507]: cannot move out of a shared reference
2+
--> $DIR/cant_move_out_of_pattern.rs:9:11
3+
|
4+
LL | match b {
5+
| ^
6+
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
12+
13+
error[E0507]: cannot move out of a shared reference
14+
--> $DIR/cant_move_out_of_pattern.rs:17:11
15+
|
16+
LL | match rc {
17+
| ^^
18+
LL |
19+
LL | deref!(x) => x,
20+
| -
21+
| |
22+
| data moved here
23+
| move occurs because `x` has type `Struct`, which does not implement the `Copy` trait
24+
25+
error: aborting due to 2 previous errors
26+
27+
For more information about this error, try `rustc --explain E0507`.

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

+10-6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
use std::rc::Rc;
66

7+
struct Struct;
8+
79
fn main() {
810
let vec: Vec<u32> = Vec::new();
911
match vec {
@@ -22,10 +24,12 @@ fn main() {
2224
deref!(1..) => {}
2325
_ => {}
2426
}
25-
// FIXME(deref_patterns): fails to typecheck because `"foo"` has type &str but deref creates a
26-
// place of type `str`.
27-
// match "foo".to_string() {
28-
// box "foo" => {}
29-
// _ => {}
30-
// }
27+
let _: &Struct = match &Rc::new(Struct) {
28+
deref!(x) => x,
29+
_ => unreachable!(),
30+
};
31+
let _: &[Struct] = match &Rc::new(vec![Struct]) {
32+
deref!(deref!(x)) => x,
33+
_ => unreachable!(),
34+
};
3135
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#![feature(deref_patterns)]
2+
#![allow(incomplete_features)]
3+
4+
fn main() {
5+
// FIXME(deref_patterns): fails to typecheck because `"foo"` has type &str but deref creates a
6+
// place of type `str`.
7+
match "foo".to_string() {
8+
deref!("foo") => {}
9+
//~^ ERROR: mismatched types
10+
_ => {}
11+
}
12+
match &"foo".to_string() {
13+
deref!("foo") => {}
14+
//~^ ERROR: mismatched types
15+
_ => {}
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/typeck_fail.rs:8:16
3+
|
4+
LL | match "foo".to_string() {
5+
| ----------------- this expression has type `String`
6+
LL | deref!("foo") => {}
7+
| ^^^^^ expected `str`, found `&str`
8+
9+
error[E0308]: mismatched types
10+
--> $DIR/typeck_fail.rs:13:16
11+
|
12+
LL | match &"foo".to_string() {
13+
| ------------------ this expression has type `&String`
14+
LL | deref!("foo") => {}
15+
| ^^^^^ expected `str`, found `&str`
16+
17+
error: aborting due to 2 previous errors
18+
19+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)