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>
This commit is contained in:
parent
a61b14d15e
commit
c623319a30
@ -711,13 +711,19 @@ fn cat_pattern_<F>(
|
|||||||
self.cat_pattern_(place_with_id, subpat, op)?;
|
self.cat_pattern_(place_with_id, subpat, op)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
PatKind::Box(subpat) | PatKind::Ref(subpat, _) | PatKind::Deref(subpat) => {
|
PatKind::Box(subpat) | PatKind::Ref(subpat, _) => {
|
||||||
// box p1, &p1, &mut p1. we can ignore the mutability of
|
// box p1, &p1, &mut p1. we can ignore the mutability of
|
||||||
// PatKind::Ref since that information is already contained
|
// PatKind::Ref since that information is already contained
|
||||||
// in the type.
|
// in the type.
|
||||||
let subplace = self.cat_deref(pat, place_with_id)?;
|
let subplace = self.cat_deref(pat, place_with_id)?;
|
||||||
self.cat_pattern_(subplace, subpat, op)?;
|
self.cat_pattern_(subplace, subpat, op)?;
|
||||||
}
|
}
|
||||||
|
PatKind::Deref(subpat) => {
|
||||||
|
let ty = self.pat_ty_adjusted(subpat)?;
|
||||||
|
// A deref pattern generates a temporary.
|
||||||
|
let place = self.cat_rvalue(pat.hir_id, ty);
|
||||||
|
self.cat_pattern_(place, subpat, op)?;
|
||||||
|
}
|
||||||
|
|
||||||
PatKind::Slice(before, ref slice, after) => {
|
PatKind::Slice(before, ref slice, after) => {
|
||||||
let Some(element_ty) = place_with_id.place.ty().builtin_index() else {
|
let Some(element_ty) = place_with_id.place.ty().builtin_index() else {
|
||||||
|
@ -1163,6 +1163,7 @@ enum TestCase<'pat, 'tcx> {
|
|||||||
Constant { value: mir::Const<'tcx> },
|
Constant { value: mir::Const<'tcx> },
|
||||||
Range(&'pat PatRange<'tcx>),
|
Range(&'pat PatRange<'tcx>),
|
||||||
Slice { len: usize, variable_length: bool },
|
Slice { len: usize, variable_length: bool },
|
||||||
|
Deref { temp: Place<'tcx> },
|
||||||
Or { pats: Box<[FlatPat<'pat, 'tcx>]> },
|
Or { pats: Box<[FlatPat<'pat, 'tcx>]> },
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1222,6 +1223,12 @@ enum TestKind<'tcx> {
|
|||||||
|
|
||||||
/// Test that the length of the slice is equal to `len`.
|
/// Test that the length of the slice is equal to `len`.
|
||||||
Len { len: u64, op: BinOp },
|
Len { len: u64, op: BinOp },
|
||||||
|
|
||||||
|
/// Call `Deref::deref` on the value.
|
||||||
|
Deref {
|
||||||
|
/// Temporary to store the result of `deref()`.
|
||||||
|
temp: Place<'tcx>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A test to perform to determine which [`Candidate`] matches a value.
|
/// A test to perform to determine which [`Candidate`] matches a value.
|
||||||
|
@ -42,6 +42,8 @@ pub(super) fn test<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> Test<
|
|||||||
TestKind::Len { len: len as u64, op }
|
TestKind::Len { len: len as u64, op }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TestCase::Deref { temp } => TestKind::Deref { temp },
|
||||||
|
|
||||||
TestCase::Or { .. } => bug!("or-patterns should have already been handled"),
|
TestCase::Or { .. } => bug!("or-patterns should have already been handled"),
|
||||||
|
|
||||||
TestCase::Irrefutable { .. } => span_bug!(
|
TestCase::Irrefutable { .. } => span_bug!(
|
||||||
@ -143,35 +145,11 @@ pub(super) fn perform_test(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
let re_erased = tcx.lifetimes.re_erased;
|
let re_erased = tcx.lifetimes.re_erased;
|
||||||
let ref_string = self.temp(Ty::new_imm_ref(tcx, re_erased, ty), test.span);
|
|
||||||
let ref_str_ty = Ty::new_imm_ref(tcx, re_erased, tcx.types.str_);
|
let ref_str_ty = Ty::new_imm_ref(tcx, re_erased, tcx.types.str_);
|
||||||
let ref_str = self.temp(ref_str_ty, test.span);
|
let ref_str = self.temp(ref_str_ty, test.span);
|
||||||
let deref = tcx.require_lang_item(LangItem::Deref, None);
|
|
||||||
let method = trait_method(tcx, deref, sym::deref, [ty]);
|
|
||||||
let eq_block = self.cfg.start_new_block();
|
let eq_block = self.cfg.start_new_block();
|
||||||
self.cfg.push_assign(
|
// `let ref_str: &str = <String as Deref>::deref(&place);`
|
||||||
block,
|
self.call_deref(block, eq_block, place, ty, ref_str, test.span);
|
||||||
source_info,
|
|
||||||
ref_string,
|
|
||||||
Rvalue::Ref(re_erased, BorrowKind::Shared, place),
|
|
||||||
);
|
|
||||||
self.cfg.terminate(
|
|
||||||
block,
|
|
||||||
source_info,
|
|
||||||
TerminatorKind::Call {
|
|
||||||
func: Operand::Constant(Box::new(ConstOperand {
|
|
||||||
span: test.span,
|
|
||||||
user_ty: None,
|
|
||||||
const_: method,
|
|
||||||
})),
|
|
||||||
args: vec![Spanned { node: Operand::Move(ref_string), span: DUMMY_SP }],
|
|
||||||
destination: ref_str,
|
|
||||||
target: Some(eq_block),
|
|
||||||
unwind: UnwindAction::Continue,
|
|
||||||
call_source: CallSource::Misc,
|
|
||||||
fn_span: source_info.span,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
self.non_scalar_compare(
|
self.non_scalar_compare(
|
||||||
eq_block,
|
eq_block,
|
||||||
success_block,
|
success_block,
|
||||||
@ -270,8 +248,56 @@ pub(super) fn perform_test(
|
|||||||
Operand::Move(expected),
|
Operand::Move(expected),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TestKind::Deref { temp } => {
|
||||||
|
let ty = place_ty.ty;
|
||||||
|
let target = target_block(TestBranch::Success);
|
||||||
|
self.call_deref(block, target, place, ty, temp, test.span);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform `let temp = <ty as Deref>::deref(&place)`.
|
||||||
|
pub(super) fn call_deref(
|
||||||
|
&mut self,
|
||||||
|
block: BasicBlock,
|
||||||
|
target_block: BasicBlock,
|
||||||
|
place: Place<'tcx>,
|
||||||
|
ty: Ty<'tcx>,
|
||||||
|
temp: Place<'tcx>,
|
||||||
|
span: Span,
|
||||||
|
) {
|
||||||
|
let source_info = self.source_info(span);
|
||||||
|
let re_erased = self.tcx.lifetimes.re_erased;
|
||||||
|
let deref = self.tcx.require_lang_item(LangItem::Deref, None);
|
||||||
|
let method = trait_method(self.tcx, deref, sym::deref, [ty]);
|
||||||
|
let ref_src = self.temp(Ty::new_imm_ref(self.tcx, re_erased, ty), span);
|
||||||
|
// `let ref_src = &src_place;`
|
||||||
|
self.cfg.push_assign(
|
||||||
|
block,
|
||||||
|
source_info,
|
||||||
|
ref_src,
|
||||||
|
Rvalue::Ref(re_erased, BorrowKind::Shared, place),
|
||||||
|
);
|
||||||
|
// `let temp = <Ty as Deref>::deref(ref_src);`
|
||||||
|
self.cfg.terminate(
|
||||||
|
block,
|
||||||
|
source_info,
|
||||||
|
TerminatorKind::Call {
|
||||||
|
func: Operand::Constant(Box::new(ConstOperand {
|
||||||
|
span,
|
||||||
|
user_ty: None,
|
||||||
|
const_: method,
|
||||||
|
})),
|
||||||
|
args: vec![Spanned { node: Operand::Move(ref_src), span }],
|
||||||
|
destination: temp,
|
||||||
|
target: Some(target_block),
|
||||||
|
unwind: UnwindAction::Continue,
|
||||||
|
call_source: CallSource::Misc,
|
||||||
|
fn_span: source_info.span,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Compare using the provided built-in comparison operator
|
/// Compare using the provided built-in comparison operator
|
||||||
fn compare(
|
fn compare(
|
||||||
@ -660,13 +686,21 @@ pub(super) fn sort_candidate(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(TestKind::Deref { temp: test_temp }, TestCase::Deref { temp })
|
||||||
|
if test_temp == temp =>
|
||||||
|
{
|
||||||
|
fully_matched = true;
|
||||||
|
Some(TestBranch::Success)
|
||||||
|
}
|
||||||
|
|
||||||
(
|
(
|
||||||
TestKind::Switch { .. }
|
TestKind::Switch { .. }
|
||||||
| TestKind::SwitchInt { .. }
|
| TestKind::SwitchInt { .. }
|
||||||
| TestKind::If
|
| TestKind::If
|
||||||
| TestKind::Len { .. }
|
| TestKind::Len { .. }
|
||||||
| TestKind::Range { .. }
|
| TestKind::Range { .. }
|
||||||
| TestKind::Eq { .. },
|
| TestKind::Eq { .. }
|
||||||
|
| TestKind::Deref { .. },
|
||||||
_,
|
_,
|
||||||
) => {
|
) => {
|
||||||
fully_matched = false;
|
fully_matched = false;
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
use rustc_infer::infer::type_variable::TypeVariableOrigin;
|
use rustc_infer::infer::type_variable::TypeVariableOrigin;
|
||||||
use rustc_middle::mir::*;
|
use rustc_middle::mir::*;
|
||||||
use rustc_middle::thir::{self, *};
|
use rustc_middle::thir::{self, *};
|
||||||
use rustc_middle::ty;
|
|
||||||
use rustc_middle::ty::TypeVisitableExt;
|
use rustc_middle::ty::TypeVisitableExt;
|
||||||
|
use rustc_middle::ty::{self, Ty};
|
||||||
|
|
||||||
impl<'a, 'tcx> Builder<'a, 'tcx> {
|
impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
pub(crate) fn field_match_pairs<'pat>(
|
pub(crate) fn field_match_pairs<'pat>(
|
||||||
@ -249,10 +249,15 @@ pub(in crate::build) fn new(
|
|||||||
default_irrefutable()
|
default_irrefutable()
|
||||||
}
|
}
|
||||||
|
|
||||||
PatKind::DerefPattern { .. } => {
|
PatKind::DerefPattern { ref subpattern } => {
|
||||||
// FIXME(deref_patterns)
|
// Create a new temporary for each deref pattern.
|
||||||
// Treat it like a wildcard for now.
|
// FIXME(deref_patterns): dedup temporaries to avoid multiple `deref()` calls?
|
||||||
default_irrefutable()
|
let temp = cx.temp(
|
||||||
|
Ty::new_imm_ref(cx.tcx, cx.tcx.lifetimes.re_erased, subpattern.ty),
|
||||||
|
pattern.span,
|
||||||
|
);
|
||||||
|
subpairs.push(MatchPair::new(PlaceBuilder::from(temp).deref(), subpattern, cx));
|
||||||
|
TestCase::Deref { temp }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
37
tests/ui/pattern/deref-patterns/bindings.rs
Normal file
37
tests/ui/pattern/deref-patterns/bindings.rs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
//@ run-pass
|
||||||
|
#![feature(deref_patterns)]
|
||||||
|
#![allow(incomplete_features)]
|
||||||
|
|
||||||
|
fn simple_vec(vec: Vec<u32>) -> u32 {
|
||||||
|
match vec {
|
||||||
|
deref!([]) => 100,
|
||||||
|
// FIXME(deref_patterns): fake borrows break guards
|
||||||
|
// deref!([x]) if x == 4 => x + 4,
|
||||||
|
deref!([x]) => x,
|
||||||
|
deref!([1, x]) => x + 200,
|
||||||
|
deref!(ref slice) => slice.iter().sum(),
|
||||||
|
_ => 2000,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nested_vec(vecvec: Vec<Vec<u32>>) -> u32 {
|
||||||
|
match vecvec {
|
||||||
|
deref!([]) => 0,
|
||||||
|
deref!([deref!([x])]) => x,
|
||||||
|
deref!([deref!([0, x]) | deref!([1, x])]) => x,
|
||||||
|
deref!([ref x]) => x.iter().sum(),
|
||||||
|
deref!([deref!([]), deref!([1, x, y])]) => y - x,
|
||||||
|
_ => 2000,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
assert_eq!(simple_vec(vec![1]), 1);
|
||||||
|
assert_eq!(simple_vec(vec![1, 2]), 202);
|
||||||
|
assert_eq!(simple_vec(vec![1, 2, 3]), 6);
|
||||||
|
|
||||||
|
assert_eq!(nested_vec(vec![vec![0, 42]]), 42);
|
||||||
|
assert_eq!(nested_vec(vec![vec![1, 42]]), 42);
|
||||||
|
assert_eq!(nested_vec(vec![vec![1, 2, 3]]), 6);
|
||||||
|
assert_eq!(nested_vec(vec![vec![], vec![1, 2, 3]]), 1);
|
||||||
|
}
|
40
tests/ui/pattern/deref-patterns/branch.rs
Normal file
40
tests/ui/pattern/deref-patterns/branch.rs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
//@ run-pass
|
||||||
|
// Test the execution of deref patterns.
|
||||||
|
#![feature(deref_patterns)]
|
||||||
|
#![allow(incomplete_features)]
|
||||||
|
|
||||||
|
fn branch(vec: Vec<u32>) -> u32 {
|
||||||
|
match vec {
|
||||||
|
deref!([]) => 0,
|
||||||
|
deref!([1, _, 3]) => 1,
|
||||||
|
deref!([2, ..]) => 2,
|
||||||
|
_ => 1000,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nested(vec: Vec<Vec<u32>>) -> u32 {
|
||||||
|
match vec {
|
||||||
|
deref!([deref!([]), ..]) => 1,
|
||||||
|
deref!([deref!([0, ..]), deref!([1, ..])]) => 2,
|
||||||
|
_ => 1000,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
assert!(matches!(Vec::<u32>::new(), deref!([])));
|
||||||
|
assert!(matches!(vec![1], deref!([1])));
|
||||||
|
assert!(matches!(&vec![1], deref!([1])));
|
||||||
|
assert!(matches!(vec![&1], deref!([1])));
|
||||||
|
assert!(matches!(vec![vec![1]], deref!([deref!([1])])));
|
||||||
|
|
||||||
|
assert_eq!(branch(vec![]), 0);
|
||||||
|
assert_eq!(branch(vec![1, 2, 3]), 1);
|
||||||
|
assert_eq!(branch(vec![3, 2, 1]), 1000);
|
||||||
|
assert_eq!(branch(vec![2]), 2);
|
||||||
|
assert_eq!(branch(vec![2, 3]), 2);
|
||||||
|
assert_eq!(branch(vec![3, 2]), 1000);
|
||||||
|
|
||||||
|
assert_eq!(nested(vec![vec![], vec![2]]), 1);
|
||||||
|
assert_eq!(nested(vec![vec![0], vec![1]]), 2);
|
||||||
|
assert_eq!(nested(vec![vec![0, 2], vec![1, 2]]), 2);
|
||||||
|
}
|
24
tests/ui/pattern/deref-patterns/cant_move_out_of_pattern.rs
Normal file
24
tests/ui/pattern/deref-patterns/cant_move_out_of_pattern.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#![feature(deref_patterns)]
|
||||||
|
#![allow(incomplete_features)]
|
||||||
|
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
struct Struct;
|
||||||
|
|
||||||
|
fn cant_move_out_box(b: Box<Struct>) -> Struct {
|
||||||
|
match b {
|
||||||
|
//~^ ERROR: cannot move out of a shared reference
|
||||||
|
deref!(x) => x,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cant_move_out_rc(rc: Rc<Struct>) -> Struct {
|
||||||
|
match rc {
|
||||||
|
//~^ ERROR: cannot move out of a shared reference
|
||||||
|
deref!(x) => x,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
@ -0,0 +1,27 @@
|
|||||||
|
error[E0507]: cannot move out of a shared reference
|
||||||
|
--> $DIR/cant_move_out_of_pattern.rs:9:11
|
||||||
|
|
|
||||||
|
LL | match b {
|
||||||
|
| ^
|
||||||
|
LL |
|
||||||
|
LL | deref!(x) => x,
|
||||||
|
| -
|
||||||
|
| |
|
||||||
|
| data moved here
|
||||||
|
| move occurs because `x` has type `Struct`, which does not implement the `Copy` trait
|
||||||
|
|
||||||
|
error[E0507]: cannot move out of a shared reference
|
||||||
|
--> $DIR/cant_move_out_of_pattern.rs:17:11
|
||||||
|
|
|
||||||
|
LL | match rc {
|
||||||
|
| ^^
|
||||||
|
LL |
|
||||||
|
LL | deref!(x) => x,
|
||||||
|
| -
|
||||||
|
| |
|
||||||
|
| data moved here
|
||||||
|
| move occurs because `x` has type `Struct`, which does not implement the `Copy` trait
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0507`.
|
@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
struct Struct;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let vec: Vec<u32> = Vec::new();
|
let vec: Vec<u32> = Vec::new();
|
||||||
match vec {
|
match vec {
|
||||||
@ -22,10 +24,12 @@ fn main() {
|
|||||||
deref!(1..) => {}
|
deref!(1..) => {}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
// FIXME(deref_patterns): fails to typecheck because `"foo"` has type &str but deref creates a
|
let _: &Struct = match &Rc::new(Struct) {
|
||||||
// place of type `str`.
|
deref!(x) => x,
|
||||||
// match "foo".to_string() {
|
_ => unreachable!(),
|
||||||
// box "foo" => {}
|
};
|
||||||
// _ => {}
|
let _: &[Struct] = match &Rc::new(vec![Struct]) {
|
||||||
// }
|
deref!(deref!(x)) => x,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
17
tests/ui/pattern/deref-patterns/typeck_fail.rs
Normal file
17
tests/ui/pattern/deref-patterns/typeck_fail.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#![feature(deref_patterns)]
|
||||||
|
#![allow(incomplete_features)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// FIXME(deref_patterns): fails to typecheck because `"foo"` has type &str but deref creates a
|
||||||
|
// place of type `str`.
|
||||||
|
match "foo".to_string() {
|
||||||
|
deref!("foo") => {}
|
||||||
|
//~^ ERROR: mismatched types
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
match &"foo".to_string() {
|
||||||
|
deref!("foo") => {}
|
||||||
|
//~^ ERROR: mismatched types
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
19
tests/ui/pattern/deref-patterns/typeck_fail.stderr
Normal file
19
tests/ui/pattern/deref-patterns/typeck_fail.stderr
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/typeck_fail.rs:8:16
|
||||||
|
|
|
||||||
|
LL | match "foo".to_string() {
|
||||||
|
| ----------------- this expression has type `String`
|
||||||
|
LL | deref!("foo") => {}
|
||||||
|
| ^^^^^ expected `str`, found `&str`
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/typeck_fail.rs:13:16
|
||||||
|
|
|
||||||
|
LL | match &"foo".to_string() {
|
||||||
|
| ------------------ this expression has type `&String`
|
||||||
|
LL | deref!("foo") => {}
|
||||||
|
| ^^^^^ expected `str`, found `&str`
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0308`.
|
Loading…
Reference in New Issue
Block a user