Skip to content

Commit dc3c094

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 <ent3rm4n@gmail.com>
1 parent a5a28e3 commit dc3c094

File tree

11 files changed

+249
-38
lines changed

11 files changed

+249
-38
lines changed

compiler/rustc_hir_typeck/src/mem_categorization.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,12 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
719719
self.cat_pattern_(place_with_id, subpat, op)?;
720720
}
721721

722+
PatKind::Box(subpat) if self.infcx.tcx.features().deref_patterns => {
723+
let ty = self.pat_ty_adjusted(pat)?;
724+
// A deref pattern generates a temporary.
725+
let place = self.cat_rvalue(pat.hir_id, ty);
726+
self.cat_pattern_(place, subpat, op)?;
727+
}
722728
PatKind::Box(subpat) | PatKind::Ref(subpat, _) => {
723729
// box p1, &p1, &mut p1. we can ignore the mutability of
724730
// PatKind::Ref since that information is already contained

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,6 +1084,7 @@ enum TestCase<'pat, 'tcx> {
10841084
Constant { value: mir::Const<'tcx> },
10851085
Range(&'pat PatRange<'tcx>),
10861086
Slice { len: usize, variable_length: bool },
1087+
Deref { temp: Place<'tcx> },
10871088
Or { pats: Box<[FlatPat<'pat, 'tcx>]> },
10881089
}
10891090

@@ -1140,6 +1141,12 @@ enum TestKind<'tcx> {
11401141

11411142
/// Test that the length of the slice is equal to `len`.
11421143
Len { len: u64, op: BinOp },
1144+
1145+
/// Call `Deref::deref` on the value.
1146+
Deref {
1147+
/// Temporary to store the result of `deref()`.
1148+
temp: Place<'tcx>,
1149+
},
11431150
}
11441151

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

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

Lines changed: 61 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
4343
TestKind::Len { len: len as u64, op }
4444
}
4545

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

4850
TestCase::Irrefutable { .. } => span_bug!(
@@ -145,35 +147,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
145147
);
146148
}
147149
let re_erased = tcx.lifetimes.re_erased;
148-
let ref_string = self.temp(Ty::new_imm_ref(tcx, re_erased, ty), test.span);
149150
let ref_str_ty = Ty::new_imm_ref(tcx, re_erased, tcx.types.str_);
150151
let ref_str = self.temp(ref_str_ty, test.span);
151-
let deref = tcx.require_lang_item(LangItem::Deref, None);
152-
let method = trait_method(tcx, deref, sym::deref, [ty]);
153152
let eq_block = self.cfg.start_new_block();
154-
self.cfg.push_assign(
155-
block,
156-
source_info,
157-
ref_string,
158-
Rvalue::Ref(re_erased, BorrowKind::Shared, place),
159-
);
160-
self.cfg.terminate(
161-
block,
162-
source_info,
163-
TerminatorKind::Call {
164-
func: Operand::Constant(Box::new(ConstOperand {
165-
span: test.span,
166-
user_ty: None,
167-
const_: method,
168-
})),
169-
args: vec![Spanned { node: Operand::Move(ref_string), span: DUMMY_SP }],
170-
destination: ref_str,
171-
target: Some(eq_block),
172-
unwind: UnwindAction::Continue,
173-
call_source: CallSource::Misc,
174-
fn_span: source_info.span,
175-
},
176-
);
153+
// `let ref_str: &str = <String as Deref>::deref(&place);`
154+
self.call_deref(block, eq_block, place, ty, ref_str, test.span);
177155
self.non_scalar_compare(
178156
eq_block,
179157
success_block,
@@ -272,9 +250,57 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
272250
Operand::Move(expected),
273251
);
274252
}
253+
254+
TestKind::Deref { temp } => {
255+
let ty = place_ty.ty;
256+
let target = target_block(TestBranch::Success);
257+
self.call_deref(block, target, place, ty, temp, test.span);
258+
}
275259
}
276260
}
277261

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

686+
(TestKind::Deref { temp: test_temp }, TestCase::Deref { temp })
687+
if test_temp == temp =>
688+
{
689+
fully_matched = true;
690+
Some(TestBranch::Success)
691+
}
692+
660693
(
661694
TestKind::Switch { .. }
662695
| TestKind::SwitchInt { .. }
663696
| TestKind::If
664697
| TestKind::Len { .. }
665698
| TestKind::Range { .. }
666-
| TestKind::Eq { .. },
699+
| TestKind::Eq { .. }
700+
| TestKind::Deref { .. },
667701
_,
668702
) => {
669703
fully_matched = false;

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

Lines changed: 10 additions & 5 deletions
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>(
@@ -257,10 +257,15 @@ impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {
257257
default_irrefutable()
258258
}
259259

260-
PatKind::DerefPattern { .. } => {
261-
// FIXME(deref_patterns)
262-
// Treat it like a wildcard for now.
263-
default_irrefutable()
260+
PatKind::DerefPattern { ref subpattern } => {
261+
// Create a new temporary.
262+
let temp = cx.temp(
263+
Ty::new_imm_ref(cx.tcx, cx.tcx.lifetimes.re_erased, subpattern.ty),
264+
pattern.span,
265+
);
266+
let temp_builder: PlaceBuilder<'tcx> = temp.into();
267+
subpairs.push(MatchPair::new(temp_builder.deref(), subpattern, cx));
268+
TestCase::Deref { temp }
264269
}
265270
};
266271

compiler/rustc_pattern_analysis/src/rustc.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
465465
PatKind::DerefPattern { .. } => {
466466
// FIXME(deref_patterns): At least detect that `box _` is irrefutable.
467467
fields = vec![];
468+
arity = 0;
468469
ctor = Opaque(OpaqueId::new());
469470
}
470471
PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => {
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#![feature(deref_patterns)]
2+
#![allow(incomplete_features)]
3+
4+
fn main() {
5+
// FIXME(deref_patterns): fix bindings wrt fake edges
6+
match vec![1] {
7+
box [] => unreachable!(),
8+
box [x] => assert_eq!(x, 1),
9+
//~^ ERROR used binding isn't initialized
10+
_ => unreachable!(),
11+
}
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0381]: used binding isn't initialized
2+
--> $DIR/bindings.rs:8:14
3+
|
4+
LL | box [x] => assert_eq!(x, 1),
5+
| -----^-
6+
| | |
7+
| | value used here but it isn't initialized
8+
| binding declared here but left uninitialized
9+
10+
error: aborting due to 1 previous error
11+
12+
For more information about this error, try `rustc --explain E0381`.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//@ run-pass
2+
#![feature(deref_patterns)]
3+
#![allow(incomplete_features)]
4+
5+
fn branch(vec: Vec<u32>) -> u32 {
6+
match vec {
7+
box [] => 0,
8+
box [1, _, 3] => 1,
9+
box [2, ..] => 2,
10+
_ => 1000,
11+
}
12+
}
13+
14+
fn nested(vec: Vec<Vec<u32>>) -> u32 {
15+
match vec {
16+
box [box [], ..] => 1,
17+
box [box [0, ..], box [1, ..]] => 2,
18+
_ => 1000,
19+
}
20+
}
21+
22+
fn main() {
23+
assert!(matches!(Vec::<u32>::new(), box []));
24+
assert!(matches!(vec![1], box [1]));
25+
assert!(matches!(&vec![1], box [1]));
26+
assert!(matches!(vec![&1], box [1]));
27+
assert!(matches!(vec![vec![1]], box [box [1]]));
28+
29+
assert_eq!(branch(vec![]), 0);
30+
assert_eq!(branch(vec![1, 2, 3]), 1);
31+
assert_eq!(branch(vec![3, 2, 1]), 1000);
32+
assert_eq!(branch(vec![2]), 2);
33+
assert_eq!(branch(vec![2, 3]), 2);
34+
assert_eq!(branch(vec![3, 2]), 1000);
35+
36+
assert_eq!(nested(vec![vec![], vec![2]]), 1);
37+
assert_eq!(nested(vec![vec![0], vec![1]]), 2);
38+
assert_eq!(nested(vec![vec![0, 2], vec![1, 2]]), 2);
39+
}

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

Lines changed: 10 additions & 6 deletions
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
box (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+
box x => x,
29+
_ => unreachable!(),
30+
};
31+
let _: &[Struct] = match &Rc::new(vec![Struct]) {
32+
box (box x) => x,
33+
_ => unreachable!(),
34+
};
3135
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#![feature(deref_patterns)]
2+
#![allow(incomplete_features)]
3+
4+
use std::rc::Rc;
5+
6+
fn main() {
7+
// FIXME(deref_patterns): fails to typecheck because `"foo"` has type &str but deref creates a
8+
// place of type `str`.
9+
match "foo".to_string() {
10+
box "foo" => {}
11+
//~^ ERROR: mismatched types
12+
_ => {}
13+
}
14+
match &"foo".to_string() {
15+
box "foo" => {}
16+
//~^ ERROR: mismatched types
17+
_ => {}
18+
}
19+
}
20+
21+
struct Struct;
22+
23+
fn cant_move_out_box(b: Box<Struct>) -> Struct {
24+
match b {
25+
//~^ ERROR: cannot move out of a shared reference
26+
box x => x,
27+
_ => unreachable!(),
28+
}
29+
}
30+
31+
fn cant_move_out_rc(rc: Rc<Struct>) -> Struct {
32+
match rc {
33+
//~^ ERROR: cannot move out of a shared reference
34+
box x => x,
35+
_ => unreachable!(),
36+
}
37+
}

0 commit comments

Comments
 (0)